Skip to content

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.

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 endpoints
  • api_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 scopes
  • authorise_all: Allow all users from this OP (default: False)
  • authorised_users: List of authorized users by ‘sub’ claim
  • authorised_vos: List of authorized Virtual Organizations
  • vo_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 accounts
  • authorise_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:

         $ pip install flaat
         $ flaat-userinfo $TOKEN
    
    where $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 Authorisation

    Info

    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 deprecated
  • primary_group: Default primary group for users
  • fallback_group: Group for users without groups (default: nogroup)
  • additional_groups: Extra groups to add users to
  • interactive: Deprecated; Always False

[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 control
  • log_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 auth
  • admin_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
  • 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 mode

    • friendly: Try preferred_username, then given_name+family_name combinations, then email
    • pooled: 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 policy
    • all: 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 groups
  • supported_groups: Regex list of allowed group names
  • method: Group name mapping method
    • classic: 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 users
  • login_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 creation
  • shadow_compatibility_function: Username compatibility method (default/v044/punch)

backend.ldap#

LDAP backend modes and settings

  • mode: LDAP access mode
    • read_only: Read existing mapped accounts only
    • pre_created: Map existing unmapped accounts
    • full_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 operations
  • admin_password: Admin password
  • user_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