Service account VPN access: certificate vs shared login
Compare a machine certificate with a stored password for unattended VPN access, and put the real permission limit in the policy that runs after login.
Two designs cover almost every unattended VPN setup: a shared human login kept in a script, or a machine certificate bound to the host. They fail in different places, which matters more than setup speed. A stored password breaks when its owner changes it or leaves; a certificate breaks when it expires or when the host is rebuilt without the key. Three conditions decide which cost you can carry, and neither design limits anything until the authorization policy does.
Start from the task, not the account
The task defines the boundary: which destinations, which services, what hours, how long a session may run. A tunnel route is only half of that boundary. The gateway decides whether the session is allowed; the resource behind it decides what the session can do. Write the list first, with a named owner, because both identity designs have to satisfy it.
Keep authentication and authorization apart while you write it down. Authentication answers who is connecting; authorization answers what the session may reach afterwards. RADIUS carries the second part as attributes such as Filter-Id, Framed-IP-Address, Session-Timeout and Class, and the gateway attaches them once the login succeeds. A design that swaps the login and leaves authorization alone hasn't limited anything.
Shared human login: the password becomes the boundary
Reusing a human account is the fastest path and the one that ages worst. The credential is a password, so it ends up in a script, an environment file and a scheduler config, and every copy needs rotation. When the owner changes it on a Friday, the job fails at the next run and nobody is watching.
MFA is where this design usually collapses. A script can't answer a push prompt, so the account gets excluded from the policy, which is exactly the account an attacker would choose. Exclusions, trusted-location bypasses and per-account exemptions multiply, and none is visible to the next person who reads the tenant settings.
Attribution is the other cost. Every session in the gateway log carries the same user name, so the nightly sync can't be told apart from the work of the person who wrote it, and revoking the job means revoking that person.
Machine certificate: identity bound to the host
A certificate changes the question from what the account knows to what the host is. With EAP-TLS the client presents a certificate and proves possession of the matching private key; the policy server maps the subject or the computer account to a group and applies the policy for that group. No prompt, no shared secret sitting in a text file.
The boundary it creates is narrower than people expect. A certificate authenticates the host, not the task, and anyone with root or local admin rights can use that key on that host. Keep the key where the platform protects it: non-exportable in the Windows certificate store, TPM-backed where the profile allows, or a root-owned 0600 file on Linux. Host compromise is credential compromise.
Expiry is the recurring failure. A certificate that expires on a Sunday stops the job on Sunday, and the gateway usually reports a generic authentication failure rather than naming the date. Check the dates from the job host as part of the runbook:
openssl x509 -in client.pem -noout -subject -issuer -dates
ipsec listcerts
Revocation needs a path the gateway checks. OpenVPN reads a CRL through the crl-verify option, and strongSwan depends on CRL or OCSP according to how the connection profile is built. If neither is wired up, removing a host means waiting for the certificate to expire.
Three conditions that decide between them
Run the comparison against the platform you have, not against a reference design.
First condition: can the gateway authorize a machine identity? If it only maps a RADIUS user name to a directory user, a certificate authenticates the host and then stalls at authorization. Second condition: does the job need resource access that only a user account carries, such as a mapped share or a per-seat licence? Then a machine identity can't substitute, and the honest answer is a dedicated user account with a scoped policy. Third condition: who can rotate the credential without a person in the loop? Automated enrollment makes certificates cheap to keep; a password known to one person does not.
| Constraint | Machine certificate | Dedicated user account with stored secret |
|---|---|---|
| Unattended login | Private key proof, no prompt | Only if the account is exempted from MFA |
| Resource expects a user identity | Works only when the resource accepts computer accounts | Matches the access a person would have |
| Rotation | Re-enrollment, often automated | Secret change plus redeploy of every copy |
| Revocation speed | Tied to CRL or OCSP refresh | Disable the account, immediate |
Read the table as a list of costs, not a ranking. The stored-secret column wins on revocation speed and on compatibility with resources that only understand user accounts; the certificate column wins on rotation and on not weakening MFA to make a script work.
Put the limit in the policy, after authentication
Whatever identity you choose, the permission limit belongs in the policy that runs after authentication. In NPS, network policies are evaluated in order and the first policy that matches every condition applies its settings, so a broad policy placed above a narrow one decides the result no matter what the client presents. Microsoft documents the condition and settings model in its NPS policy overview.
Give the job its own group or policy condition, based on something the machine identity carries: a certificate template, a calling station identifier, or an address from a dedicated pool. Then bound the session with attributes: a Filter-Id for the role your gateway maps it to, a short Session-Timeout so a stalled job re-authenticates instead of holding a session for days, and a Framed-IP-Address from a pool you firewall on the far side. Application authorization stays separate: a tunnel to a database server is not permission to read a table inside it.
Verify refusals, then plan removal
Access that works proves nothing about least privilege. Test the refusals instead: connect with the job identity and reach for a destination outside the list, open a second concurrent session if the gateway is meant to allow only one, and repeat the login after revoking the credential.
Then confirm the same session in two places. The gateway log shows the identity that authenticated; the policy server log shows which policy matched and which attributes it returned. On OpenVPN the server records the certificate common name, and ipsec statusall on strongSwan prints the established tunnel with the identity in use. If the two logs disagree about the identity, the policy is matching something other than what you assume.
Removal belongs in the design, not a cleanup sprint. Write down everything to delete (certificate or account, group membership, network policy, pool entry, firewall rule on the target) and run it once while the job is easy to rebuild.
If you are starting today, work in this order: write the resource list, pick the identity with the three conditions, put the limit in the policy, and exercise one denial before the job goes live.