Skip to content

Latest commit

 

History

History
330 lines (236 loc) · 14.2 KB

File metadata and controls

330 lines (236 loc) · 14.2 KB

LemonLDAP::NG Configuration

Before deploying the PAM module on your servers, you need to configure LemonLDAP::NG.

Step 1: Install the Plugins

The Open Bastion plugins are available from the Linagora plugin store.

Option A: Debian/Ubuntu (recommended)

Add the Linagora plugin repository and install:

# Add repository key
curl -fsSL https://linagora.github.io/lemonldap-ng-plugins/store-key.asc \
  | sudo gpg --dearmor -o /usr/share/keyrings/linagora-llng-plugins.gpg

# Add repository
echo "deb [signed-by=/usr/share/keyrings/linagora-llng-plugins.gpg] https://linagora.github.io/lemonldap-ng-plugins/debian stable main" \
  | sudo tee /etc/apt/sources.list.d/llng-plugins.list

# Install (pick pam-access and/or ssh-ca depending on your auth mode)
sudo apt-get update
sudo apt-get install lemonldap-ng-plugin-oidc-device-authorization \
                     lemonldap-ng-plugin-oidc-device-organization \
                     lemonldap-ng-plugin-pam-access \  # token-based auth
                     lemonldap-ng-plugin-ssh-ca         # certificate-based auth

Option B: Docker

The yadd/lemonldap-ng-portal images already include all plugins. No extra installation needed — just enable them via customPlugins (see Step 3).

Plugins used by Open Bastion

  • OIDCDeviceAuthorization - Server enrollment via OAuth 2.0 Device Authorization Grant (RFC 8628)
  • OIDCDeviceOrganization - Extension for organizational device enrollment (tokens identify the device, not the approving admin)
  • PamAccess (optional) - Token-based authentication: authorization endpoints (/pam/authorize, /pam/bastion-token)
  • SSHCA (optional) - Certificate-based authentication: SSH Certificate Authority

You need at least one of PamAccess or SSHCA depending on your authentication mode. See PAM Authentication Modes.

Step 2: Create the OIDC Relying Party

In the LLNG Manager, create a new OIDC Relying Party:

  1. Go to OpenID Connect Relying PartiesAdd
  2. Configure:
    • Client ID: pam-access
    • Client secret: Generate a strong secret
    • Allowed grant types: Enable device_code (for server enrollment)
    • Allowed scopes: openid, pam:server

Step 3: Enable the Plugins

Use customPlugins inside lemonldap-ng.ini, section [portal]:

  • Token-based authentication (PamAccess only):
[portal]
customPlugins = ::Plugins::OIDCDeviceAuthorization, ::Plugins::OIDCDeviceOrganization, ::Plugins::PamAccess
  • Certificate-based authentication (SSHCA only):
[portal]
customPlugins = ::Plugins::OIDCDeviceAuthorization, ::Plugins::OIDCDeviceOrganization, ::Plugins::SSHCA
  • Both:
[portal]
customPlugins = ::Plugins::OIDCDeviceAuthorization, ::Plugins::OIDCDeviceOrganization, ::Plugins::PamAccess, ::Plugins::SSHCA

Step 4: Plugin Parameters

Additional and optional parameters that can be inserted into lemonldap-ng.ini, section [portal]:

General Parameters

Parameter Default Description
oidcServiceDeviceAuthorizationExpiration 600 (10mn) Device authorization expiration time
oidcServiceDeviceAuthorizationPollingInterval 5 Polling interval in seconds (clients polling faster get slow_down errors)
oidcServiceDeviceAuthorizationUserCodeLength 8 Length of user code (base-20 charset, collision-safe)
portalDisplayPamAccess 0 Set to 1 (or a rule) to display PAM tab
pamAccessRp pam-access OIDC Relying Party name
pamAccessTokenDuration 600 (10mn) Token duration
pamAccessMaxDuration 3600 (1h) Maximum token duration
pamAccessExportedVars {} Exported variables
pamAccessOfflineTtl 86400 (1d) Offline cache TTL
pamAccessSshRules {} SSH access rules
pamAccessServerGroups {} Server groups configuration
pamAccessSudoRules {} Sudo rules
pamAccessOfflineEnabled 0 Enable offline mode
pamAccessHeartbeatInterval 300 (5mn) Heartbeat interval
pamAccessManagedGroups {} Unix groups managed by LLNG per server group (see Group Synchronization)

Per-RP Device Authorization Parameters

These are set in the LLNG Manager on each OIDC Relying Party:

Parameter Default Description
oidcRPMetaDataOptionsAllowDeviceAuthorization 0 Enable device grant. Can be a rule expression to restrict who can approve
oidcRPMetaDataOptionsDeviceOwnership (empty) Set to organization for organizational device mode (see below)

Organizational Device Enrollment

When oidcRPMetaDataOptionsDeviceOwnership is set to organization on an RP, the OIDCDeviceOrganization plugin changes the device authorization behavior:

  • An administrator approves the device normally through the /device page
  • The resulting tokens identify the client application (client_id) instead of the approving admin
  • The device token survives the admin's session expiration or account removal
  • Refresh tokens remain valid independently of the admin's session

This is useful for enrolling servers, kiosks, or IoT devices that belong to the organization rather than a specific user.

Device Authorization Security Features

  • CSRF protection: the /device verification form uses a one-time token
  • Rate limiting: clients polling faster than the configured interval receive slow_down errors with incremental backoff
  • User code collision detection: codes are regenerated on collision (up to 10 retries)
  • Per-RP access rules: AllowDeviceAuthorization accepts boolean expressions to restrict which users can approve devices
  • CrowdSec integration: invalid user_code attempts are reported to CrowdSec (scenario llng/device-auth-bruteforce)

When offline mode is enabled, the server-side cache is protected by Cache Brute-Force Protection.

SSH CA Parameters (optional)

Parameter Default Description
portalDisplaySshCa 0 Set to 1 (or a rule) to display SSHCA tab
sshCaCertMaxValidity 365 (1y) Maximum certificate validity
sshCaSerialPath "" Path for certificate serial storage
sshCaPrincipalSources $uid Principal sources
sshCaKrlPath "" Path for Key Revocation List

Step 4.1: Generate and Import the SSH CA Key (optional)

If you're using the SSH CA plugin for key-based authentication, you need to generate a CA key pair and import it into LemonLDAP::NG.

Generate the SSH CA Key Pair

# Generate Ed25519 CA key pair (recommended)
openssl genpkey -algorithm ed25519 -out ssh-ca.key
openssl pkey -in ssh-ca.key -pubout -out ssh-ca.pub

# Display keys for import into LLNG Manager
echo "=== Private Key (copy this) ==="
cat ssh-ca.key
echo "=== Public Key (copy this) ==="
cat ssh-ca.pub

Alternatively, for compatibility with older systems, use RSA:

openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:4096 -out ssh-ca.key
openssl pkey -in ssh-ca.key -pubout -out ssh-ca.pub

Import the Key into LLNG

Via Manager (LemonLDAP::NG >= 2.22)

  1. Go to General ParametersKeysAdd a key
  2. Set a key name (e.g., ssh-ca)
  3. Paste the private key content into Private key
  4. Paste the public key content into Public key
  5. Save the configuration

Then configure the SSH CA plugin to use this key inside lemonldap-ng.ini, section [portal]:

[portal]
sshCaKeyRef = ssh-ca

Via lemonldap-ng.ini

Insert this into lemonldap-ng.ini, section [portal]:

[portal]
keys = { ssh-ca => { keyPublic => "<public key value>", keyPrivate => "<private key value>" } }
sshCaKeyRef = ssh-ca

Create directories for SSH CA state files

sudo mkdir -p /var/lib/lemonldap-ng/ssh
sudo chown www-data:www-data /var/lib/lemonldap-ng/ssh

These directories store the certificate serial number counter and the Key Revocation List (KRL).

Step 5: Restart LemonLDAP::NG

sudo systemctl restart lemonldap-ng-fastcgi-server
# or
sudo systemctl restart apache2  # if using mod_perl

Server Groups

Server groups allow different authorization rules for different server categories.

Configure in lemonldap-ng.ini

In /etc/lemonldap-ng/lemonldap-ng.ini, section [portal]:

[portal]
pamAccessServerGroups = { \
    production => '$hGroup->{ops}', \
    staging    => '$hGroup->{ops} or $hGroup->{dev}', \
    dev        => '$hGroup->{dev}', \
    default    => '1' \
}

Configure on Each Server

In /etc/open-bastion/openbastion.conf:

server_group = production

Or during enrollment:

sudo ob-enroll -g production

Group Synchronization

The group synchronization feature (#38) allows LemonLDAP::NG to manage Unix supplementary groups on target servers. When a user connects via SSH, their Unix groups are synchronized with the groups defined in LLNG.

Configuration

In lemonldap-ng.ini, configure which groups LLNG should manage for each server group:

pamAccessManagedGroups = {
    production => 'docker,developers,readonly',
    staging => 'developers,testers',
    bastion => 'operators,auditors',
    default => ''
}
  • Groups listed in pamAccessManagedGroups will be created automatically on the server if they don't exist
  • Users are added to groups they're assigned to in LLNG
  • Users are removed from managed groups they're no longer assigned to in LLNG
  • Groups NOT in pamAccessManagedGroups are never modified (local groups are preserved)

How It Works

sequenceDiagram
    participant Client as SSH Client
    participant Server as Server (PAM)
    participant LLNG as LemonLDAP::NG

    Client->>Server: ssh user@server
    Server->>LLNG: /pam/authorize
    LLNG-->>Server: {groups: ["dev","docker"],<br/>managed_groups: ["dev","docker","qa"]}
    Note over Server: Filter by local whitelist<br/>(if configured)
    Note over Server: Sync groups:<br/>• Add user to "dev", "docker"<br/>• Remove from "qa" (managed but not assigned)
    Server-->>Client: Session established
Loading

Security Considerations

  • Principle of least privilege: Don't include privileged groups (sudo, wheel, admin) in managed_groups
  • Audit trail: All group modifications are logged with event type GROUP_SYNC
  • Offline behavior: Group sync uses cached group information when LLNG is unreachable
  • File protection: Group modifications use system tools (groupadd, gpasswd) which handle /etc/group and /etc/gshadow atomically

Local Whitelist (Defense-in-Depth)

Administrators can optionally configure a local whitelist of groups allowed to be managed on each server. This provides defense-in-depth by restricting which groups LLNG can actually modify, regardless of what managed_groups it sends.

In /etc/open-bastion/openbastion.conf:

# Only allow these groups to be managed by LLNG on this server
allowed_managed_groups = docker,developers,readonly

When configured:

  • Groups must be in BOTH pamAccessManagedGroups (from LLNG) AND allowed_managed_groups (local) to be synced
  • Groups sent by LLNG but not in the local whitelist are silently ignored
  • This allows local administrators to have final control over which groups can be managed

Use cases:

  • Restrict LLNG to manage only specific groups on sensitive servers
  • Allow different group policies per server even within the same server group
  • Provide a safety net against misconfigured LLNG policies

Example: Per-Environment Groups

# Developer groups differ by environment
pamAccessManagedGroups = {
    production => 'app-users,readonly',           # Read-only in prod
    staging => 'app-users,developers,docker',     # Full dev access in staging
    bastion => 'operators'                        # Bastion operators only
}

When a user moves from staging to production access, their docker and developers group memberships are automatically removed on production servers.

See Also