Custom App with Reverse Proxy

Introduction

The Groove.id proxy is a small application that will sit in front of your application. It will receive requests for your app, make sure the users are properly identified, and pass the request on to your application.

Diagram

In a typical scenario you will run the proxy on the same host that runs your application. The proxy listens for incoming requests, makes sure they are from valid users, and then passes those requests on to your application.

The Groove.id reverse proxy sits in front of your custom web application and filters incoming requests. By default it makes sure that all incoming requests have been authenticated using Groove.id before your application even sees the request.

Using this model, you don’t have to worry about the nitty-gritty of correctly implementing authentication protocols, and your application is protected from unauthenticated requests.

Your application can then inspect the request headers to determine which user has been authenticated and collect other biographical information.

Getting Started

In the Groove.id console, create a new proxy application.

If you haven’t already, install the client software with

curl -sL https://signin.example.com/get.sh | bash

Setup

Copy the token parameter and pass it the the proxy:

$ grooveid proxy --token=$YOUR_TOKEN

By default the proxy will listen on tcp/443 and tcp/80 and will proxy requests to localhost:8000. It will automatically obtain a TLS certificate from Lets Encrypt if possible.

Handling Requests

The proxy will ensure that each request comes from a valid, authenticated user. Your upstream application receives information about the remote user via HTTP headers that are added to the request. An example request might look like:

Authorization: Basic cGdAaW5pdGVjaC5iaXo6
X-Forwarded-For: 127.0.0.1
X-Grooveid-Access-Check: http://myapp.example.com:8000/__grooveid/check_access?pt=HvLhvlczrbZD9v
X-Grooveid-Email: pg@initech.biz
X-Grooveid-Full-Name: Peter Gibbons
X-Grooveid-Session-Expires: Fri, 27 Jul 2018 16:27:22 GMT
X-Grooveid-User-Name: pg
X-Grooveid-User: OtczVCbP71qDGQ
X-Grooveid-Auth-Type: session
X-Remote-User: pg@initech.biz

The headers added to the request are:

  • X-Grooveid-User-Name - the unix user name of the remote user.
  • X-Grooveid-Email - the user’s email address. (also included as X-Remote-User)
  • X-Grooveid-Full-Name - the user’s full name
  • X-Grooveid-User - a globally unique identifier for the user which will remain unchanged even if the email address or user name changes.
  • X-Grooveid-Session-Expires - The time when the user’s session expires
  • X-Grooveid-Auth-Type - The kind of proof that was provided. bearer means that an API key was used. session means that a short-lived signed cookie corresponding to a new or existing session was presented.
  • X-Forwarded-For - the remote address of the request
  • X-Grooveid-Access-Check - The URL for checking access during the request.
  • Authorization - a synthetic basic authorization header where the username is the user’s email address and the password is blank.

Access Check

The upstream server receives a URL in the X-Grooveid-Access-Check request header. During the request, you may query this URL to determine if the remote user is a member of a particular group. An example request:

POST /__grooveid/check_access?pt=HvLhvlczrbZD9v HTTP/1.1
Host: myapp.example.com:8000
Content-Type: application/x-www-form-urlencoded
Content-Length: 114

group=eQPSVFjWJda1PF&group=om23cRgmpKt7A5&next=http%3A%2F%2Fmyapp.example.com%3A8000%2Fsome%2Fdeep%2Flink

The proxy server will respond with 200 Ok if the remote user is a member of each of the groups specified. Otherwise, the proxy server will respond with 403 Forbidden.

The forbidden response will include a Location response header. If you direct the user to that URL they will be prompted to join or activate the group. When the process completes, the user will be sent to the url given in the next parameter to the initial access check request.

The access check endpoint accepts the following parameters:

  • group - which group to check (may be specified multiple times)
  • next - A URL to direct the user back to when the access request flow completes.
  • op - If “or” is specified then access to any of the groups is sufficient to allow access. If “and” is specified, then the user must be a member of all groups. The default is “and.”

Note: The access check URL is valid only while the request is being processed. After the request is complete, the access check URL is invalid.

This endpoint also returns a JSON body that includes the status information about each group:

[
  {
    "GroupID": "Z9Or13VAbDKGMP",
    "StatusCode": 401,
    "Location": "https://signin.initech.biz/groups/join?code=Group+Membership&data=..."
  },
  {
    "GroupID": "uGHRZBr98fWqCw",
    "StatusCode": 200
  }
]

Note: When op=or and the user is already an active member of one of the groups you provided, then because of the short-circuit evaluation, other groups will not be checked, and thus will not appear in the returned list. There is no short-circuit evaluation when op=and is given.

Configuration

You can configure the proxy with a configuration file, with environment variables, or command line flags.

Example configuration file:

[proxy]
listen = :443
tls = on
cert = /var/secrets/tls.cert
key = /var/secrets/tls.key
upstream = "http://localhost:8000"

[auth]
token = "6b3xeo0mthu8wy.o9wpCxLMUtSKNF.OEI2YjESGkR01noWtjgH9CBv3bQH"

[redirect]
listen = :80

[path "/public"]
        auth = none

[path "/private"]
        auth = required

[path "/api"]
        auth = optional

[path "/admin"]
        auth = required
        upstream = http://localhost:9999

Specify the configuration with the -C flag, for example:

grooveid proxy -C /etc/grooveid-proxy.conf

The same configuration using the command line (and defaults):

grooveid proxy \
    --listen :443 \
    --cert /var/secrets/tls.cert \
    --key /var/secrets/tls.key \
    --upstream http://localhost:8000 \
    --token 6b3xeo0mthu8wy.o9wpCxLMUtSKNF.OEI2YjESGkR01noWtjgH9CBv3bQH \
    --redirect-listen :80 \
    -c 'path_/public_auth=none' \
    -c 'path_/private_auth=required' \
    -c 'path_/api_auth=optional' \
    -c 'path_/admin_auth=required' \
    -c 'path_/admin_upstream=http://localhost:9999'

The same configuration using environment variables:

export GV_PROXY_LISTEN=:443
export GV_PROXY_CERT=/var/secrets/tls.cert
export GV_PROXY_KEY=/var/secrets/tls.key
export GV_PROXY_UPSTREAM=http://localhost:8000
export GV_AUTH_TOKEN=6b3xeo0mthu8wy.o9wpCxLMUtSKNF.OEI2YjESGkR01noWtjgH9CBv3bQH
export GV_REDIRECT_LISTEN=:80
export GV_PATH_/public_AUTH=none
export GV_PATH_/private_AUTH=required
export GV_PATH_/api_AUTH=optional
export GV_PATH_/admin_AUTH=required
export GV_PATH_/admin_UPSTREAM=http://localhost:9999

Settings

The following settings can be used to configure the proxy.

proxy.listen

Environment Variable: GV_PROXY_LISTEN

Commandline Flag: --listen

Default: :443

Specifies the address and port where the proxy interface should listen.

proxy.tls

Section: proxy

Name: tls

Environment Variable: GV_PROXY_TLS

Commandline Flag: --tls

Default: true

If true then the proxy should listen for encrypted requests, otherwise the requests are expected to be plaintext.

proxy.cert

Section: proxy

Name: cert

Environment Variable: GV_PROXY_CERT

Commandline Flag: --cert

Default: empty

If this and proxy.key are specified then serve requests over TLS using the key and certificate. If not specified, then use the ACME protocol to obtain a certificate on demand.

proxy.key

Section: proxy

Name: key

Environment Variable: GV_PROXY_KEY

Commandline Flag: --key

Default: empty

If this and proxy.cert are specified then serve requests over TLS using the key and certificate. If not specified, then use the ACME protocol to obtain a certificate on demand.

proxy.name

Section: proxy

Name: name

Environment Variable: GV_PROXY_NAME

Commandline Flag: --name

Default: empty

Specifies the host name of the server. If not specified, then the server name is determined the first time a request is received.

proxy.auth

Section: proxy

Name: auth

Environment Variable: GV_PROXY_AUTH

Commandline Flag: --auth

Default: required

Specifies the default authentication mode for the server, which must be one of:

  • none - no authentication is performed.
  • optional - when authentication information is present, verify it and pass it to the upstream server, but do not require it.
  • required - the remote user must be authenticated in order to pass a request to the upstream server.

Note: This setting may be overridden for a particular path prefix in a prefix section.

proxy.upstream

Section: proxy

Name: upstream

Environment Variable: GV_PROXY_UPSTREAM

Commandline Flag: --upstream

Default: http://localhost:8000

Specifies the default upstream server for the proxy.

Note: This setting may be overridden for a particular path prefix in a prefix section.

proxy.privateaddress

Section: proxy

Name: privateaddress

Environment Variable: GV_PROXY_PRIVATEADDRESS

Commandline Flag: --privateaddress

Default: (empty)

Specifies the address that the upstream service uses to make metadata requests, such as access checks, while processing the request. If not specified, the URL will be constructed from the inbound request. Change this setting if the URL in the X-Grooveid-Access header is incorrect.

proxy.cookiekey

Section: proxy

Name: cookiekey

Environment Variable: GV_PROXY_COOKIEKEY

Commandline Flag: --cookiekey

Default: (empty)

Specifies the key used to sign session cookies. If not specified, a key will be generated at startup. This must be a PEM-encoded ECDSA private key on the P-256 curve.

proxy.cookiedomain

Section: proxy

Name: cookiedomain

Environment Variable: GV_PROXY_COOKIEDOMAIN

Default: (empty)

Specifies the domain to be used when issuing session cookies.

auth.token

Section: auth

Name: token

Environment Variable: GV_AUTH_TOKEN

Commandline Flag: --token

Default: (empty)

Specifies the Groove.id token that associates this proxy with an application in Groove.id. Get this value from the Groove.id console.

auth.server

Section: auth

Name: server

Environment Variable: GV_AUTH_SERVER

Commandline Flag: --auth-server

Default: https://auth.groove.id

Specifies the address of the Groove.id API server. Under normal circumstances, this should not need to change.

prefix.\$prefix

You can override the default settings for a particular URL prefix by specifying a prefix section. When processing a request, the longest prefix that matches is used.

Example:

[proxy]
auth = required

[prefix /public]
auth = none

[prefix /api]
auth = optional

prefix.\$prefix.auth

Section: prefix

Subsection: a URL path prefix

Name: auth

Overrides the authentication mode specified by proxy.auth for this prefix.

prefix.\$prefix.upstream

Section: prefix

Subsection: a URL path prefix

Name: upstream

Overrides the upstream server specified by proxy.upstream for this prefix.

redirect.listen

Section: redirect

Name: listen

Environment Variable: GV_REDIRECT_LISTEN

Commandline Flag: --redirect-listen

Default: :80

The redirector is used to redirect plaintext http requests to encrypted https requests. This option specified the address and port where the plaintext redirector should listen.

acme.server

Section: acme

Name: server

Environment Variable: GV_ACME_SERVER

Commandline Flag: --acme-server

Default: https://acme-v01.api.letsencrypt.org/directory

Specifies the ACME protocol server to use to issue certificates on demand. For testing use the Let’s Encrypt staging server https://acme-staging.api.letsencrypt.org/directory

acme.email

Section: acme

Name: email

Environment Variable: GV_ACME_EMAIL

Commandline Flag: --acme-email

Default: (empty)

Specifies the email address to use when registering a certificate using the ACME protocol. By default it derives the email address from the server name given in proxy.name.

API Access

You can use Groove.id API keys to access your application. To create an API key, select the API Keys tab, then Add.

Create an API key

Click the pencil icon next to Scopes and assign your new API key at least the user scope. Click Save.

Assign Scopes to the API key

You can specify the API key in the Authorization header, as the username or password in HTTP basic authentication.

Using the authorization header:

curl -H "Authorization: Bearer gv000000your00key00goes00here000000000000000000000000000" https://myapp.example.com:4343/
GET / HTTP/1.1
Host: myapp.example.com
Authorization: Bearer gv000000your00key00goes00here000000000000000000000000000

Using the username field in basic authentication:

curl -u "gv000000your00key00goes00here000000000000000000000000000:" https://myapp.example.com:4343/