> For the complete documentation index, see [llms.txt](https://mariadb.com/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://mariadb.com/docs/maxscale/reference/maxscale-authenticators/maxscale-pam-authenticator.md).

# MaxScale PAM Authenticator

Authenticate users via Pluggable Authentication Modules (PAM). This reference details the PAMAuth module, supporting standard passwords, two-factor authentication, and SUID mode.

## Overview

Pluggable authentication module (PAM) is a general purpose authentication API. An application using PAM can authenticate a user without knowledge about the underlying authentication implementation. The actual authentication scheme is defined in the operating system PAM config (e.g. `/etc/pam.d/`), and can be quite elaborate.

## Configuration

The MaxScale PAM module requires minimal configuration in typical use cases. Enable it by changing the listener authenticator module to "pamauth".

```ini
[Read-Write-Listener]
type=listener
address=::
service=Read-Write-Service
authenticator=pamauth
```

Enable multiple authenticators by separating them with a comma:

```ini
[Read-Write-Listener]
type=listener
address=::
service=Read-Write-Service
authenticator=mariadbauth,pamauth
```

When PAM authenticator is enabled, MaxScale uses it to authenticate users with *plugin* set to `pam` in the `mysql.user`-table. MaxScale reads the PAM service name for the user from the `authentication_string` column. When the client logs in, MaxScale uses the matching PAM service configuration for authenticating the user. If the `authentication_string` for a user is empty, MaxScale uses the fallback service "mysql".

PAM service configuration is out of the scope of this document, see [The Linux-PAM System Administrators' Guide](https://www.linux-pam.org/Linux-PAM-html/Linux-PAM_SAG.html) for more information. A simple service definition useful for testing is below.

```ini
auth            required        pam_unix.so
account         required        pam_unix.so
```

## Settings

#### `pam_use_cleartext_plugin`

* Type: [boolean](/docs/maxscale/maxscale-management/deployment/installation-and-configuration/maxscale-configuration-guide.md#booleans)
* Mandatory: No
* Dynamic: No
* Default: `false`
* Description: Enables cleartext password authentication between the client and MaxScale when using PAM.

If enabled, MaxScale communicates with the client as if using [mysql\_clear\_password](https://github.com/mariadb-corporation/mariadb-docs/tree/main/server/reference/clientserver-protocol/1-connecting/connection.md#mysql_clear_password-plugin). This setting has no effect on MaxScale-to-backend communication, as that adapts to either `dialog` or `mysql_clear_password` depending on which one the backend suggests. This setting is meant to be used with the similarly named MariaDB Server setting.

```
authenticator_options=pam_use_cleartext_plugin=1
```

#### `pam_mode`

* Type: [enumeration](/docs/maxscale/maxscale-management/deployment/installation-and-configuration/maxscale-configuration-guide.md#enumerations)
* Mandatory: No
* Dynamic: No
* Values: `password`, `password_2FA`, `suid`
* Default: `suid`
* Description: Defines the PAM authentication mode for client login.

This setting defines the authentication mode used. The supported values are:

* `password` Normal password-based authentication
* `password_2FA` Password + 2FA-code based authentication
* `suid` Authenticate using suid sandbox subprocess

{% tabs %}
{% tab title="Current" %}
The default value of `pam_mode` is `suid`, as it is the most flexible option.
{% endtab %}

{% tab title="< 26.10" %}
The default value of `pam_mode` is `password`.
{% endtab %}
{% endtabs %}

```
authenticator_options=pam_mode=password_2FA
```

If set to `password`, MaxScale asks for a password from incoming clients authenticating via PAM. MaxScale uses the password when either the local PAM API or a backend asks for "Password". MaxScale considers any other query (e.g. "Verification code") an error. This mode runs synchronously, meaning that PAM authentication blocks the entire working thread for the duration of the authentication. This can be especially problematic if the authentication is slow (e.g. a slow LDAP server), as this can cause MaxScale to crash due to watchdog timeout. See [the limitations-section](#implementation-details-and-limitations) for more details.

If set to `password_2FA`, MaxScale asks for two passwords from incoming clients authenticating via PAM ("Password" and "Verification code"). MaxScale uses the normal password when either the local PAM API or a backend asks for "Password". MaxScale answers any other password prompt (e.g. "Verification code") with the second password. This mode runs synchronously, similarly to `authenticator_options=pam_mode=password`. Two-factor mode is incompatible with `pam_use_cleartext_plugin`.

If set to `suid`, MaxScale launches a separate subprocess for every client to handle PAM authentication. This subprocess runs the binary *maxscale\_pam\_auth\_tool* (installed in the binary directory), which calls the system PAM libraries. The binary is installed with the SUID bit set, which means that it runs with root-privileges regardless of the user launching it. This should bypass any file grant issues (e.g. reading `etc/shadow`) that may arise with the `password` or `password_2FA` options. The `suid`-option runs PAM for different clients asynchronously, which avoids blocking the MaxScale worker thread. It may also resist buggy PAM plugins crashing, as the crash would be limited to the subprocess only. The MariaDB Server uses a similar PAM authentication implementation. `suid` mode supports two-factor authentication.

#### `pam_backend_mapping`

* Type: [enumeration](/docs/maxscale/maxscale-management/deployment/installation-and-configuration/maxscale-configuration-guide.md#enumerations)
* Mandatory: No
* Dynamic: No
* Values: `none`, `mariadb`
* Default: `none`
* Description: Manages the mapping of PAM-authenticated users to backend authentication.

Defines backend authentication mapping, i.e. switch of authentication method between client-to-MaxScale and MaxScale-to-backend. Supported values:

* `none` No mapping
* `mariadb` Map users to normal MariaDB accounts

```ini
authenticator_options=pam_backend_mapping=mariadb
```

If set to "mariadb", MaxScale will authenticate clients to backends using standard MariaDB authentication. Authentication to MaxScale itself still uses PAM. MaxScale asks the local PAM system if the client username was mapped to another username during authentication, and use the mapped username when logging in to backends. Passwords for the mapped users can be given in a file, see `pam_mapped_pw_file` below. If passwords are not given, MaxScale will try to authenticate without a password. Because of this, normal PAM users and mapped users cannot be used on the same listener.

Because the client still needs to authenticate to MaxScale normally, an anonymous user may be required. If the backends do not allow such a user, one can be manually added using the service setting [user\_accounts\_file](/docs/maxscale/maxscale-management/deployment/installation-and-configuration/maxscale-configuration-guide.md#user_accounts_file).

To map usernames, the PAM service needs to use a module such as \_user\_map.so\_. This module is not a standard Linux component and needs to be installed separately. It is included in recent MariaDB Server packages and can also be compiled from source. See [user mapping](/docs/maxscale/reference/maxscale-listeners.md#user_mapping_file) for more information on how to configure the module. If the goal is to only map users from PAM to MariaDB in MaxScale, then configuring user mapping on just the machine running MaxScale is enough.

Instead of using `pam_backend_mapping`, consider using the listener setting [user\_mapping\_file](/docs/maxscale/reference/maxscale-listeners.md#user_mapping_file), as it is easier to configure. `pam_backend_mapping` should only be used when the user mapping needs to be defined by pam.

#### `pam_mapped_pw_file`

* Type: path
* Mandatory: No
* Dynamic: No
* Default: None
* Description: Specifies a JSON file containing passwords for PAM-mapped users.

Path to a json-text file with user passwords. Default value is empty, which disables the feature.

```ini
authenticator_options=pam_mapped_pw_file=/home/root/passwords.json,pam_backend_mapping=mariadb
```

This feature only works together with `pam_backend_mapping=mariadb`. The file is only read during listener creation (typically MaxScale start) or when a listener is modified during runtime. The file should contain passwords for the mapped users. When a client is authenticating, MaxScale searches the password data for a matching username. If one is found, MaxScale uses the supplied password when logging in to backends. Otherwise, MaxScale tries to authenticate without a password.

One array, "users\_and\_passwords", is read from the file. Each array element in the array must define the following fields:

* "user": String. Mapped client username.
* "password": String. Backend server password. Can be encrypted with *maxpasswd*.

An example file is below.

```json
{
    "users_and_passwords": [
        {
            "user": "my_mapped_user1",
            "password": "my_mapped_pw1"
        },
        {
            "user": "my_mapped_user2",
            "password": "A6D4C53619FFFF4DF252A0E595EDB0A12CA44E16AF154D0ED08F687E81604BFF42218B4EBA9F3EF8D907CF35E74ABDAA"
        }
    ]
}
```

## Anonymous User Mapping

When backend authenticator mapping is not in use (`authenticator_options=pam_backend_mapping=none`), the PAM authenticator supports a limited version of [user mapping](/docs/maxscale/reference/maxscale-listeners.md#user_mapping_file). It requires less configuration but is also less accurate than proper mapping. Anonymous mapping is enabled in MaxScale if the following user exists:

* Empty username (e.g. `''@'%'` or `''@'myhost.com'`)
* `plugin = 'pam'`
* Proxy grant is on (The query `SHOW GRANTS FOR user@host;` returns at least one row with `GRANT PROXY ON ...`)

When the authenticator detects such users, anonymous account mapping is enabled for the hosts of the anonymous users. To verify this, enable the info log (`log_info=1` in MaxScale config file). When a client is logging in using the anonymous user account, MaxScale logs a message starting with "Found matching anonymous user ...".

When mapping is on, the MaxScale PAM authenticator does not require client accounts to exist in the `mysql.user` table received from the backend. MaxScale only requires that the hostname of the incoming client matches the host field of one of the anonymous users (comparison performed using `LIKE`). If a match is found, MaxScale attempts to authenticate the client to the local machine with the username and password supplied. The PAM service used for authentication is read from the `authentication_string` field of the anonymous user. If authentication was successful, MaxScale then uses the username and password to log to the backends.

Anonymous mapping is only attempted if the client username is not found in the `mysql.user` table as explained in [Configuration](#configuration). This means, that if a user is found and the authentication fails, anonymous authentication is not attempted even when it could use a different PAM service with a different outcome.

Setting up PAM group mapping for the MariaDB server is a more involved process as the server requires details on which Unix user or group is mapped to which MariaDB user. See [this guide](https://github.com/mariadb-corporation/mariadb-docs/tree/main/server/reference/plugins/authentication-plugins/authentication-with-pluggable-authentication-modules-pam/configuring-pam-authentication-and-user-mapping-with-unix-authentication.md) for more details. Performing all the steps in the guide also on the MaxScale machine is not required, as the MaxScale PAM plugin only checks that the client host matches an anonymous user and that the client (with the username and password it provided) can log into the local PAM configuration. If using normal password authentication, simply generating the Unix user and password should be enough.

## Implementation Details and Limitations

The general PAM authentication scheme is difficult for a proxy such as MaxScale. An application using the PAM interface needs to define a *conversation function* to allow the OS PAM modules to communicate with the client, possibly exchanging multiple messages. This works when a client logs in to a single normal server, as the server can just relay the PAM messages to the client and relay the client's answers to PAM. MaxScale, however, needs to be able to autonomously log into multiple backends. This means that the messages and answers need to be predefined. The passwords given to MaxScale need to work as is when MaxScale logs into the backends. This requirement prevents the use of one-time passwords.

The MaxScale PAM authentication module supports two password modes. In normal mode, client authentication begins with MaxScale sending an AuthSwitchRequest packet. In addition to the command, the packet contains the client plugin name ("dialog" or "mysql\_clear\_password"), a message type byte (4) and the message "Password: ". The client should respond with the password, which MaxScale will forward to PAM running on the local machine. If the password is correct, MaxScale sends an OK-packet to the client. If the local PAM asks for additional credentials as is typical in two-factor authentication schemes, authentication fails. Informational messages such as password expiration notifications are allowed. MaxScale simply prints these to the log.

On the backend side, MaxScale expects the servers to act as MaxScale did towards the client. The servers should send an AuthSwitchRequest packet as defined above, MaxScale responds with the password received from the client, and finally backend replies with OK. MaxScale prints informational messages from backends to the info-log, if enabled.

### Two-factor Authentication Support

MaxScale supports a limited form of two-factor authentication with the `pam_mode=password_2FA`-option. Since MaxScale uses the 2FA-code given by the client to log in to the local PAM API as well as all the backends, the code must be reusable. This prevents the use of any kind of centrally checked one-use codes. Time-based codes work, assuming the backends are checking the codes independently of each other. Automatic reconnection features (e.g. in readwritesplit router) do not work, as the code has likely changed since original authentication.

Optionally, the PAM configuration on the backend servers can be weakened such that the servers only asks for the normal password. This way, MaxScale checks the 2FA-code of the incoming client, while MaxScale logs into the backends using only the password.

MaxScale does not forward the password prompts from the PAM API to the client when `pam_mode=password_2FA`. MaxScale will always ask for "Password" and "Verification code", even if the PAM API asks for other items. This prevents the use of authentication schemes where a specific question must be answered (e.g. "Input code Nr. 5"). This is not a significant limitation, as such schemes would not work with backend servers anyway.

### SUID-mode

In `pam_mode=suid`, MaxScale relays the messages from the PAM API to the client, which allows for more flexibility with different password types. MaxScale still assumes that at most two passwords are used. MaxScale records the password answers of the client and uses them to answer password queries when logging in to backend servers. MaxScale assumes that the first answer from the client is a normal password and the second answer is a 2FA-code.

## Test Tool

MaxScale binary directory contains the *test\_pam\_login*-executable. This simple program asks for a username, password and PAM service and then uses the given credentials to log in to the given service. `test_pam_login` uses the same code as MaxScale itself to communicate with the OS PAM interface and may be useful for diagnosing PAM login issues.

<sub>*This page is licensed: CC BY-SA / Gnu FDL*</sub>

{% @marketo/form formId="4316" %}
