A user was added on the server, the password was typed correctly, and the connection still ends with AUTH_FAILED. The plugin line usually explains why: the word after the plugin path is a PAM service name, and it decides which stack of modules answers the question. It is not a username, and it is not a pointer to a password file that OpenVPN reads itself.

What the second word selects

The configuration line takes a plugin path and one argument:

plugin /usr/lib/openvpn/plugins/openvpn-plugin-auth-pam.so openvpn

That argument is passed to the PAM library as the service name, which makes the stack at /etc/pam.d/openvpn the one that runs. Write login there and you get the interactive login policy instead, with whatever modules that policy happens to include on your distribution.

Use the plugin path the installed package provides rather than a path copied from another host. The filename and directory differ between distributions, and a wrong path fails at startup with a plugin load error rather than at authentication time.

Where the stack actually reads accounts

PAM is a dispatcher. The service file lists modules in order, and the first one that can answer decides the outcome. A stack calling pam_unix reads local accounts; a stack calling an LDAP or RADIUS module sends the credentials somewhere else entirely.

Read the file the service name resolves to, including anything it includes:

cat /etc/pam.d/openvpn
grep -H pam_ /etc/pam.d/openvpn /etc/pam.d/common-auth

If the stack delegates to a directory, adding a local account changes nothing. If it reads local accounts, creating the user in the directory changes nothing either. The account has to exist where the stack looks, and that is the whole diagnosis in most of these reports.

One attempt, two logs

Correlate a single failed connection across both sides. The OpenVPN log shows the plugin result and the client's rejection; the system authentication log shows which module ran and what it decided. Reading them in the same time window is what separates a missing service file from a backend timeout. A gap in the authentication log means the stack was never reached at all.

journalctl -u openvpn-server@server --since "-2 min"
journalctl -t sshd -t sudo --since "-2 min"

Test the service directly with a utility that speaks PAM, so you are not debugging the VPN and the stack at the same time. A successful direct test with the same service name and account narrows the problem to how the daemon passes credentials, not to the account itself.

pamtester openvpn alice authenticate

If the direct test fails with the same credentials that work at a console, the stack is not reading the account source you assumed. If it succeeds, look at the conversation the plugin runs and at what the client actually sent. Run it once with a deliberately wrong password as well, so you can see the difference between a refusal and a module error. Those two outcomes look identical in the VPN log and completely different here.

A dedicated service beats editing login

Give the VPN its own service file when its requirements differ from interactive login, which they usually do. Editing login or common-auth changes the policy for every program that includes them, and the next person to debug an unrelated login problem inherits your change.

Copy the modules you need into a small service file, keep the ordering explicit, and note who owns it. That file is the thing to check when a password reset appears to have no effect.

What the client sends, and what it does not

The client supplies a username and password through its supported prompt or file. The server plugin passes those values into the PAM conversation; it does not read them from the server's configuration. A server-side line that looks like it defines a password is not doing that, and no profile edit will create an account.

Username and password can coexist with client certificates, and enabling PAM is not a reason to stop verifying certificates. Keep the layers the deployment intended and fix only the account source.

Acceptance is two-sided: the intended account succeeds, and an account that should be refused still fails. The auth-pam plugin README documents the service argument and the backends it can front, which is the place to confirm the arrangement you are trying to build.

Amanqephe ahlobene