AuthZ mediator
This repository contains authorization mediator implementation for Apertis Platform infrastructure.
Prerequisites
Dependencies
Configuration
Required
AuthZ mediator requires following parameters to run:
- OIDC Client secret declared in OIDC Provider
- Self listen address (to pass it to the service that requires authorization)
- OIDC Provider URL (used for token origin verification)
- Service authorization callback URL (to redirect tokens)
- Membership mapping backend
When using GitLab group membership mapping backend an API token is required as well.
Both OIDC Client secret and GitLab API token are expected to be set in AuthZ mediator environment (as AUTHZ_MEDIATOR_SECRET
and GITLAB_SECRET
, respectively).
Remaining required parameters can be set either in the configuration file or in the environment (environment takes precedence over configuration file):
Parameter | Configuration | Environment |
---|---|---|
Provider URL | provider | AUTHZ_MEDIATOR_PROVIDER |
Self address | mediator | AUTHZ_MEDIATOR_SELF |
Callback URL | redirect | AUTHZ_MEDIATOR_REDIRECT |
Mapping backend | backend | AUTHZ_MEDIATOR_BACKEND |
Configuration file path can be passed to the AuthZ mediator as an argument for -config
flag.
Configuration file example is available in authz-mediator.yaml.sample
.
Optional
Additional parameters can be used to adjust AuthZ mediator to custom needs:
- Mediator ID (to register OIDC Client on OIDC Provider)
- Extend scope request with
groups
(YAML boolean: yes/true or no/false) - API calls retries number to verify user presence (integer)
- API calls interval to verify user presence (integer; milliseconds)
- Port for the mediator to listen on
- Group mapping: if specified, groups listed here are renamed according to the mapping
- Groups the logging in user must be a member of (glob-style patterns are accepted; processed after the mapping)
- Groups to be synchronised ("included groups"): only the groups matching one or more of the patterns are be processed; if no pattern is specified, nothing will be processed at all.
- Whether group membership should be enforced by periodically re-checking it against the source and blocking users
when they’re no longer members of the required groups (available for MS Graph API only). The interval can be specified
as an integer with a duration unit such as
s
,m
,h
:10s
,15m
,3h
. The shortest allowed interval is one minute. - When enforcing group membership, rules shouldn’t apply to the mediator’s own user, if one exists — this username
defaults to
mediator
, but can be specified in the configuration file only. If the mediator doesn’t use a dedicated user, set the username to any invalid username, e.g.!
.
These parameters can also be set either in the configuration file or in the environment (environment takes precedence over configuration file):
Parameter | Configuration | Environment |
---|---|---|
Mediator ID | id |
AUTHZ_MEDIATOR_ID |
Groups Scope | groups |
AUTHZ_MEDIATOR_GROUPS |
API retries | retries |
AUTHZ_MEDIATOR_RETRIES |
API backoff | backoff |
AUTHZ_MEDIATOR_BACKOFF |
Server port | port |
AUTHZ_MEDIATOR_PORT |
Group mapping | group_map |
AUTHZ_MEDIATOR_GROUP_MAP |
Included groups | included_groups |
AUTHZ_MEDIATOR_INCLUDED_GROUPS |
Required groups | required_groups |
AUTHZ_MEDIATOR_REQUIRED_GROUPS |
Enforce groups | enforce_groups |
AUTHZ_MEDIATOR_ENFORCE_GROUPS |
Enforcement interval | enforce_groups_interval |
AUTHZ_MEDIATOR_ENFORCE_GROUPS_INTERVAL |
Usernames to be skipped | enforce_groups_ignore_users |
AUTHZ_MEDIATOR_ENFORCE_GROUPS_IGNORE_USERS |
Don't actually lock users | enforce_groups_audit_only |
AUTHZ_MEDIATOR_ENFORCE_GROUPS_AUDIT_ONLY |
Mediator username | mediator_username |
AUTHZ_MEDIATOR_USERNAME |
When the group mapping is passed through the environment, use YAML/JSON mapping notation. Group lists are passed as space-separated lists.
Extending scope request can also be set by a command line flag -groups
which takes precedence over environment variable and configuration file.
Standalone deployment example
Have a look at docker-compose.yaml
file. It can be used an evaluation environment for AuthZ mediator.
Key parameters to adjust are described below.
Dex IdP
OIDC Provider:Mount dex.config.tmpl
as /etc/dex/config.docker.yaml
and use following environment:
DEX_ISSUER=http://${oidc-provider}:${oidc-provider-port:-5556}/dex
DEX_STATIC_CLIENT_ID=authz-mediator
DEX_STATIC_CLIENT_REDIRECT_URI=http://${mediator}:${mediator-port:-5555}/callback
DEX_STATIC_CLIENT_NAME='Authorization mediator'
DEX_STATIC_CLIENT_SECRET=${MEDIATOR_SECRET}
Remember to open ${oidc-provider-port}
declared in DEX_ISSUER
.
GitLab
Service requiring authorization:Seed GitLab configuration using following example:
GITLAB_HTTPS: 'false'
GITLAB_ROOT_PASSWORD: ${GITLAB_PASSWD}
GITLAB_OMNIBUS_CONFIG: |
external_url 'http://gitlab'
gitlab_rails['omniauth_allow_single_sign_on'] = ['openid_connect']
gitlab_rails['omniauth_block_auto_created_users'] = false
gitlab_rails['omniauth_auto_link_user'] = ['openid_connect']
gitlab_rails['omniauth_providers'] = [
{
'name' => 'openid_connect',
'label' => 'Test OpenID-Connect',
'args' => {
'name' => 'openid_connect',
'scope' => ['openid', 'profile', 'email'],
'response_type' => 'code',
'issuer' => 'http://${oidc-provider}:${oidc-provider-port:-5556}/dex',
'discovery' => false, # GitLab discovery expects HTTPS
'uid_field' => 'preferred_username',
'client_options' => {
'identifier' => 'authz-mediator',
'secret' => '${MEDIATOR_SECRET}',
'redirect_uri' => 'http://${mediator}:${mediator-port:-5555}/callback',
'authorization_endpoint' => 'http://${mediator}:${mediator-port:-5555}/auth',
'token_endpoint' => 'http://${mediator}:${mediator-port:-5555}/token',
'userinfo_endpoint' => 'http://${mediator}:${mediator-port:-5555}/userinfo',
'jwks_uri' => 'http://${mediator}:${mediator-port:-5555}/keys'
}
}
}
]
Note that for the OpenID-Connect spec
basic
is the default client auth method if no different mechanism is
configured at the client registration time.
Quoting the GitLab documentation:
client_auth_method
(optional) specifies the method used for authenticating the client with the OpenID Connect provider.
- Supported values are:
basic
- HTTP Basic Authentication.jwt_bearer
- JWT-based authentication (private key and client secret signing).mtls
- Mutual TLS or X.509 certificate validation.- Any other value posts the client ID and secret in the request body.
- If not specified, this value defaults to
basic
.
Remember to open 80
port and create API token for later use by the AuthZ mediator.
AuthZ mediator
Use following environment (when working with Dex IdP and GitLab):
GITLAB_SECRET=${GITLAB_API_TOKEN}
AUTHZ_MEDIATOR_SECRET=${MEDIATOR_SECRET}
AUTHZ_MEDIATOR_SELF=http://${mediator}:${mediator-port:-5555}
AUTHZ_MEDIATOR_PROVIDER=http://${oidc-provider}:${oidc-provider-port:-5556}/dex
AUTHZ_MEDIATOR_REDIRECT=http://${service:-gitlab}/${authz-callback:-users/auth/openid_connect/callback}
Remember to open ${mediator-port}
declared in AUTHZ_MEDIATOR_SELF
.
High level design
AuthZ mediator consists of three base components:
- Main command which collects mediator configuration and reports fatal conditions if any
- Five handlers for relaying traffic between OIDC Provider and a service that requires authorization
- Membership mapping backend for supported service
Relayed traffic is snooped twice:
- To add request for
groups
scope (if required and explicitly set in mediator configuration) - modifies traffic - To extract code required for token exchange - read-only, not affecting traffic
Membership mapping is run as a separate goroutine from request handling and does not affect user authorization. Please note that this requires additional checks for user availibility in membership mapping because it may start before user initialization is completed.
Implementation overview
Handlers
The main goal for handler functions was to be transparent for both OIDC Provider and the service. This is why there are as few interactions as possible with the relayed traffic. Handlers mostly ensure that no important information is missing from passed requests.
Currently only ID token is used for membership mapping.
Additional information can be read from mediator.TokenResponse
returned from mediator.(*Mediator).exchange()
.
Service API
Service support is provided by a pair of structures:
- Low-level service API client
- High-level service logic handler (implementing
mediator.MediatorBackend
interface)
This way handling service logic can be tested without actual service instance but with a mockup instead. Reducing pairs of structures to simpler architecture with single mapper and service clients with unified error handling could require too much resources when number of supported backends increases.
Configuration options
When introducing new configuration options it is worth to consider:
- Can it be safely put in the configuration file?
- Can it be assigned a sane default value?
Getting values from environmental variables is encouraged for easy cloud-native migration.