A certificate that validates against a shared authority has passed a test of trust, not a test of membership. Those two decisions are often collapsed because they both happen at connect time, and the collapse stays invisible until someone who should not be on a service is admitted to it. The fix is not a second authority; it is a second decision, made per service and recorded where the daemon reads it. Keep the two apart and most of the confusion in this area disappears.

A trusted chain says nothing about service membership

Two services can accept the same issuing hierarchy and still have different populations. One laptop belongs on the first and must not reach the second, while other clients of the same hierarchy do belong there. Removing the authority from that service would lock out everyone who legitimately uses it, so the work is admission rather than trust.

It helps to picture two locks on one door. The certificate opens the outer lock, and a per-service decision opens the inner one; neither substitutes for the other. A service with only the outer lock is not enforcing membership at all, it is merely authenticating. Write both decisions down before editing anything, because a policy that exists only in the shape of a working connection cannot be audited. What follows concerns the inner lock.

Turn optional overrides into an admission gate

A client configuration directory is normally a convenience: files in it override settings for the clients they name, and a client without a file simply runs with the server defaults. One directive converts that convenience into a gate, so that a missing file means the connection is refused.

client-config-dir /etc/openvpn/service-a/clients
ccd-exclusive

Community OpenVPN 2.6 documents ccd-exclusive in its manual page as exactly this condition, and the named-file lookup keys off the effective client identity. Other builds and product-managed servers may differ, so confirm the installed version before relying on the same semantics elsewhere.

Treat the path above as an example and check the deployed layout rather than creating a directory a guide invented. Leave the existing certificate checks, server identity checks and account authentication exactly as they were, because the fragment adds a condition and removes nothing. Stage the change before production, since a refusal raised at the wrong point looks like a certificate failure and sends the investigation to the authority. Confirm afterwards that the log line for a refused client names the missing configuration file rather than the chain.

One directory per service, not one shared list

Membership belongs to the service that enforces it, so each service keeps its own directory and its own files. Pointing two services at one list makes them share membership whether or not anybody intended it, and the resulting change is hard to trace back to a decision. The shared authority can stay shared; the admission files cannot. Keeping them separate also means a mistake in one service cannot silently widen another.

A DEFAULT entry is the case that misleads people. It supplies fallback settings when no named file is found, but it is not the named file the gate requires, so it cannot admit a client on its own. Test a client that has no entry at all and confirm the refusal; if that client is admitted, the fallback is being applied and ccd-exclusive is not in force in that instance.

The effective identity decides which file is read

The lookup uses the identity the client actually authenticated as, which is normally the certificate common name. Where the server also verifies a username and password and maps that name into the common name, the file must match the authenticated username instead. A mismatch leaves a directory full of plausible filenames that never get selected.

Reading is the other half. The daemon usually drops privileges after startup, so the reduced account needs read access to the directory and every entry in it. Clients must not be able to create their own entry through any writable path, because self-service admission defeats the mechanism entirely. Check the mount and share permissions and not only the file mode, since a directory that looks closed to one account can be open to another. Then confirm which account the running process really uses, rather than which account the unit file mentions. Those two are the same only when nothing else changed them.

Observe refusal, admission and revocation separately

Try an identity that belongs on the service and confirm it connects. Try a valid certificate with no entry and confirm it is refused. Try an identity admitted to the other service and confirm it is not admitted here. Record the service instance and the effective identity next to each result, because a result without that context cannot be reproduced by anyone else.

Withdrawal is a separate operation from revocation. Editing an admission file removes one service entitlement while the identity stays valid everywhere else, whereas a compromised key needs the organisation revocation response. Decide in advance what happens to sessions that are already up, since neither a directory edit nor a revocation list ends them by itself.

Admission is also only the first gate. Joining a service says nothing about the subnets, databases or management interfaces behind it, and that resource policy has to be written and tested on its own.

Skyldar greinar