Configuration
Overview#
The different configuration files serve different purposes:
Each file has a specific role in the authentication and user management pipeline, working together to provide OIDC-based SSH access.
/etc/motley_cue/motley_cue.conf
: Main configuration (WHO can access) (general | authorisation)/etc/motley_cue/feudal_adapter.conf
: Backend user management config (HOW users are created)/etc/motley_cue/flaat.conf
: OIDC validation config (WHICH OPs are supported)/etc/nginx/sites-available/nginx.motley_cue
: WHERE service is accessible-
/etc/pam.d/
: PAM configs Multiple files for optional PAM integration -
/etc/gunicorn/gunicorn.conf.py
: Gunicorn server config
motley_cue.conf#
The main configuration file is generally self documenting: it contains examples for typical use. Configuration is structured into several sections as follows.
General config#
[mapper] - Core service settings:
log_level
: Logging level (default: WARNING)log_file
: Log file location (default: /dev/stderr)enable_docs
: Enable Swagger/ReDoc documentation (default: False)docs_url
,redoc_url
: Documentation endpointsapi_version
: API version to use (default: v1)
[mapper.otp] - One-Time Password settings:
This section configures creation and storage of short lived one time passwords (OTPs).
When using PAM authentication in ssh-oidc, the OIDC access token is passed by the ssh-client through the password field. Some ssh-client have a length limitation of 1kb for the password field. Hence, Motley Cue supports the dynamic creation of one time passwords, that clients can use instead of large access tokens:
use_otp
: Use OTP instead of tokens as SSH password (default: True)backend
: Storage backend - memory, sqlite, sqlitedict (default: memory)db_location
: Token database location (default: /tmp/tokenmap.db)keyfile
: Encryption key file (default: /tmp/motley_cue.key)
Example:
[mapper.otp]
# Enable OTP for tokens > 1KB
use_otp = True
# Backend for storing OTP-AT mapping
backend = sqlite
# Database location
db_location = /run/motley_cue/tokenmap.db
# Encryption key file
keyfile = /run/motley_cue/motley_cue.key
[privacy] - Privacy policy:
privacy_contact
: Service operator email (MUST be configured)privacy_file
: Privacy statement location (default: /etc/motley_cue/privacystatement.md)
Authorisation#
Authorisation is configured in one or more [authorisation.*] sections. It contains per-OP authorisation.
Defaults for the authorisation.*
sections can be set in the magic
section [DEFAULT]
. These options are available and explained below.
op_url
: OpenID Provider URL (required)scopes
: Required OIDC scopesauthorise_all
: Allow all users from this OP (default: False)authorised_users
: List of authorized users by ‘sub’ claimauthorised_vos
: List of authorized Virtual Organizationsvo_claim
: OIDC claim containing VOs (default: eduperson_entitlement)vo_match
: How many VOs to match - all, one, or integer (default: one)audience
: Audience claim for this service (optional)authorised_admins
: Admin users who can suspend/resume accountsauthorise_admins_for_all_ops
: Allow admins to manage users from any OP
Note
- The ‘op_url’ field is mandatory for each authorisation section
- Unknown claims in tokens are ignored (won’t cause authorisation failure)
- We use delegated Access Tokens I.e. there is no need to register
a
client_id/client_secret
with a provider. - Each provider must support
jwt
access tokens, that contain information about who issued an access token.
[DEFAULT]
section#
The [DEFAULT]
section sets the defaults for all
[authorisation.<provider_name>]
sections. These options are supported:
-
scopes
- The required scopes from the OP to access the service
default:scopes = ["openid", "profile", "email", "eduperson_entitlement"]
-
authorise_all
- Authorise all users from trusted OP.
default:authorise_all = False
-
authorised_vos
- List of VOs whose members are authorised to use the service
default:authorised_vos []
-
vo_claim
- the OIDC claim containing the VOs specified above
default:vo_claim = eduperson_entitlement
-
vo_match
- how many VOs need to be matched from the list, valid options: all, one, or an int
default:vo_match = one
-
authorised_users
= list of individual users authorised to use the
service specified through OIDC ‘sub’, relative to the section’s OP defaults to empy list if not specified
default:authorised_users = []
Info
You can find out your
sub
and VOs you are a member of by using flaat:where$ pip install flaat $ flaat-userinfo $TOKEN
$TOKEN
contains an Access Token from the OP you are interested it.A commandline tool to retrieve access tokens is: [oidc-agent][https://github.com/indigo-dc/oidc-agent]
-
audience
- audience claim specific to this service (OPTIONAL); it can
be a string or a list of strings if empty or not specified, audience checking will not be used for authorisation
Be aware that many OPs do not support this feature, in which case it will be ignored, and not be used for authorisation.
default:audience = ""
-
authorised_admins
- Admin AuthorisationInfo
Each OP includes authorisation for the
/admin
endpoint, which allows suspending and resuming access for individual users.
Only individual admins can be authorised (by ‘sub’ claim). This is meant for infrastructure security contacts. In the future, we plan to support this feature for VO managers too, to suspend members of their own VO (by using VO roles encoded in AARC-G002 entitlements).default:
authorised_admins = []
[authorisation.<provider_name>]
#
Each [authorisation.<provider_name>]
section is used to configure one OP
(OpenID Provider). You can set the provider_name
yourself.
Each [authorisation.<provider_name>]
section overrides the
any of the [DEFAULT]
options. the op_url
must be specified.
op_url
- OIDC provider URL (mandatory)
Examples#
Basic example#
Here is an example for how to configure the EGI Checkin OP to authorise any user of
the VO identified by its entitlement
"urn:mace:egi.eu:group:my-group:role=member"
. In addition, also allow th
user "ea1b8e21-3654-4178-bd04-98c6adf58951"
known at the EGI OP.
[authorisation.egi]
# The complete URL for the OIDC provider (MANDATORY)
op_url = https://aai.egi.eu/auth/realms/egi
# Authorise based on Virtual Organisation membership (entitlements)
authorised_vos = ["urn:mace:egi.eu:group:my-group:role=member"]
vo_claim = eduperson_entitlement
# Authorise specific individual users
authorised_users = ["ea1b8e21-3654-4178-bd04-98c6adf58951"]
For requiring users that have shown their passport at their Home-IdP, you
can put this into feudal_adapter.conf
.
[assurance]
prefix = https://refeds.org/assurance/
require = profile/espresso | profile/cappuccino
Multiple Identity Providers#
You can configure different authorisation rules for each provider:
[authorisation.kit]
op_url = https://oidc.scc.kit.edu/auth/realms/kit
# Only allow members of KIT
vo_claim = eduperson_scoped_affiliation
authorised_vos = ["member@kit.edu", "student@kit.edu"]
[authorisation.google]
op_url = https://accounts.google.com
# Only allow specific email domains
vo_claim = email
vo_list = [".*@example\.com"]
[authorisation.helmholtz]
op_url = https://login.helmholtz.de/oauth2/
# Require specific entitlements (from the default claim: eduperson_entitlement)
authorised_vos = ["urn:geant:helmholtz.de:group:my-project:role=member"]
feudal_adapter.conf#
Feudal andles the backend user management configuration and the just-in-time provisioning of accounts. It is very flexible.
Feudal supports several so called “backends” for the account creation. Also, different strategies for generating local usernames is supported. Existing usernames are always preferred, and are automatically found, when an existing mapping to them exists.
To support use-cases in which an adminstrator needs to approve new users before admins triggers a provisioning flow themselves, a so called “approval flow” is supported.
Provisioning#
feudal_adapter.conf
is structured in sections as follows.
[ldf_adapter]
#
Core backend settings of the leudal_adapter
(also ldf_adapter
).
backend
: Backend type - local_unix, bwidm, ldap (default: local_unix)backend_supports_preferring_existing_user
: Detect and reuse existing usernames (default: False) To be deprecatedprimary_group
: Default primary group for usersfallback_group
: Group for users without groups (default: nogroup)additional_groups
: Extra groups to add users tointeractive
: Deprecated; AlwaysFalse
[messages]
#
Select which information will be logged
log_file
: Log file location (default: /var/log/feudal/feudal.log)log_level
: Logging level (default: INFO)log_to_console
: Console logging controllog_name_changes
: Log username/group name normalization (default: True)log_primary_group_definition
: Log primary group selection (default: True)log_username_creation
: Log username generation attempts (default: False)
[approval]
#
Configure the approval workflow for deploying local accounts. When active, instead of adding the user to a backend, the administrator is notified and asked to create the account manually.
Until the admin approved the account, pending users will be shown a message to return later.
enabled
: Require admin approval for accounts (default: False)user_db_location
: Pending requests database, sqlite (/var/lib/feudal/pending_users.db)notifier
: Notification method (default: email)
[notifier.email]
#
Email notification settings
smtp_server
: SMTP server hostname (default: localhost)smtp_port
: SMTP server port (default: 25)use_ssl
: Enable SSL/TLS (default: False)sent_from
: Sender email address (default: admin@localhost)sent_from_password
: Sender password for authadmin_email
: Admin recipient (default: admin@localhost)templates_dir
: Email template directory (default: /etc/feudal/templates)
[assurance]
#
User assurance requirements
prefix
: Assurance claim prefix (default: https://refeds.org/assurance/)require
: Boolean expression of required assurance levels- Operators:
&
(AND),|
(OR),()
(grouping) - Special:
+
(any claim),*
(always satisfied) - Example:
profile/espresso | IAP/medium & ID/eppn-unique-no-reassign
- Deafult:
profile/cappuccino
- Operators:
verified_undeploy
: Block undeployment for unauthorized users (default: false)
Example:
[assurance]
prefix = https://refeds.org/assurance/
require = profile/espresso |
IAP/medium & ID/eppn-unique-no-reassign |
IAP/low & ID/eppn-unique-no-reassign |
https://aai.egi.eu/LoA#Substantial |
profile/cappuccino
[username_generator]
#
We support several different username creation strategies.
These modes are currently available:
- friendly: Friendly implements a list of strategies that are tried one
after another. The input is based on different claims of the incoming
userinfo object:
- preferred_username claim (if present)
- combine a varying number of letters of given_name + family_name
- email-address if all else fails
-
pooled: Pooled implements the pool-account behaviour known from “the grid”. By default, we use the primary group name and append the digits, in the order of incoming users. In practice, group names may be much longer. Therefore, a
prefix
may be configured. -
mode
: Username generation modefriendly
: Try preferred_username, then given_name+family_name combinations, then emailpooled
: Use group prefix + sequential numbers (grid-style)
pool_prefix
: Prefix for pooled accounts (default: primary group name)pool_digits
: Number of digits for pooled accounts (default: 3)strip_sub_groups
: Remove subgroups from long federated names (default: no) This may be useful in situations where you receive very long group names. (See the [groups
] section for a better way.)
[groups]
#
Configure how entitlements
and groups
are being translated into local
unix names, and whether to create them at all. This is probably the most
specific part about feudal.
Warning
If you change the group mapping after the first groups have been created, new groups according to the new configuration could be created. Please carefully review your configuration e.g. by using tools such as xxxxxxxxxxxxxx FIXME xxxxxxxxxxxxx
policy
: Group creation policyall
: Create all groups from user attributes (potentially many)listed
: Only create groups in supported_entitlements/supported_groups
supported_entitlements
: Regex list of allowed entitlement-based groupssupported_groups
: Regex list of allowed group namesmethod
: Group name mapping methodclassic
: Default mapping (pre-feudal-v1 compatibility)regex
: Custom regex-based mapping rules
mapping
: List of regex replacement rules (for method=regex)
Example: Only allow specific entitlements#
[groups]
policy = listed
supported_entitlements =
urn:mace:egi.eu:group:eosc-synergy.eu.*
urn:mace:egi.eu:group:vo.example.org:role=member
urn:geant:helmholtz.de:group:.*
Example: Only allow specific group names#
[groups]
policy = listed
supported_groups =
users
developers
test-vo-.*
wlcg-.*
Example: Allow any group, but map it#
For this we use regular expressions in the mapping
option. This allows
automatically generating all groups that come in, but make sure they are
mapped to short and digestable names.
[groups]
policy = all
method = regex
mapping =
# Remove role information
:role=(owner|member) ->
# Shorten long prefixes
urn:mace:egi.eu:group: -> egi_
urn:geant:helmholtz.de:group: -> helm_
# Replace dots and colons with hyphens
[\.:] -> -
# Specific renamings
eosc-synergy.eu -> synergy
data.kit.edu -> kit
# Shorten admin groups
:admins -> :adm
Example: Map supported entitlements to groups#
xxxxxxxxxxxxxxxxx FIXME: is this actually correct? xxxxxxxxxxxxxx
[groups]
policy = listed
supported_entitlements =
urn:mace:egi.eu:group:eosc-synergy.eu.*
urn:mace:egi.eu:group:vo.example.org:role=member
urn:geant:helmholtz.de:group:.*
method = regex
mapping =
urn:mace:egi.eu:group:eosc-synergy.eu. -> synergy_
urn:mace:egi.eu:group:vo.example.org:role=member -> example
urn:geant:helmholtz.de:group:.* -> helmholtz
login_info
#
User login information display
description
: Service description shown to userslogin_help
: Login instructions template- Additional custom fields supported
backend.local_unix
#
Local Unix backend
shell
: User shell (default: /bin/bash)home_base
: Home directory base (default: /home)deploy_user_ssh_keys
: Deploy SSH keys from OIDC claims (default: no)punch4nfdi
: Use PUNCH4NFDI-specific group name translation (default: no)post_create_script
: Script to run after user creationshadow_compatibility_function
: Username compatibility method (default/v044/punch)
backend.ldap
#
LDAP backend modes and settings
mode
: LDAP access moderead_only
: Read existing mapped accounts onlypre_created
: Map existing unmapped accountsfull_access
: Create and manage accounts
host
: LDAP server hostname (default: localhost)port
: LDAP server port (default: 1389, 636 for TLS)tls
: Enable LDAPS protocol (default: False)admin_user
: Admin DN for write operationsadmin_password
: Admin passworduser_base
: User search base (default: ou=users,dc=example)group_base
: Group search base (default: ou=groups,dc=example)attribute_oidc_uid
: OIDC UID attribute (default: gecos)attribute_local_uid
: Local UID attribute (default: uid)- For full_access mode:
shell
: User shell (default: /bin/sh)home_base
: Home directory base (default: /home)uid_min
,uid_max
: UID range (default: 1000-60000)gid_min
,gid_max
: GID range (default: 1000-60000)post_create_script
: Post-creation script
flaat.conf#
Not yet used in practice
nginx
reverse proxy setup#
Our packages ship with a default configuration file that installs to
/etc/nginx/sites-enabled/nginx.motley_cue
server {
listen 8080;
listen [::]:8080;
server_name _;
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
# we don't want nginx trying to do something clever with
# redirects, since we set the Host: header above already.
proxy_redirect off;
proxy_pass http://unix:/run/motley_cue/motley-cue.sock;
}
}
Please refer to the nginx documentation for setting up further aspects:
- HTTPS termination
- Port configuration
- Host certificates
- URL routing to motley-cue service
PAM integration with SSH#
PAM integration with SSH is done via pam-ssh-oidc
Note
The PAM integration requires our PAM module on the ssh serverside. If
this is not acceptable, the oinit
set of tools (also part of our
tools) works with ssh-certificates
and does not require PAM modules.
Last change: Aug 18, 2025 11:21:32