AllowUsers and DenyUsers in sshd_config don’t actually restrict login access; they filter it after the SSH daemon has already accepted the connection and authenticated the user.
Let’s see this in action. Imagine a user alice who is allowed to log in, and bob who is not.
Here’s a snippet of /etc/ssh/sshd_config:
AllowUsers alice
Now, if alice tries to log in:
ssh alice@your_server_ip
She’ll get a password prompt and can log in.
If bob tries to log in:
ssh bob@your_server_ip
He’ll get an immediate "Permission denied" message.
But here’s the crucial part: sshd still processed Bob’s initial connection and authentication attempt. The filtering happens after the daemon has already done the heavy lifting of accepting the TCP connection and verifying Bob’s credentials (password, key, etc.). The AllowUsers/DenyUsers directives are the last gatekeepers, but they don’t stop the initial handshake or authentication process itself.
The problem AllowUsers and DenyUsers solve is simple: controlling who can get a shell on the server via SSH. It’s a blunt instrument for user-based access control. The system works by sshd reading the sshd_config file on startup. When a new connection comes in, sshd first performs its standard authentication mechanisms. If authentication is successful, it then consults the AllowUsers and DenyUsers directives. If the authenticated username matches an entry in AllowUsers (and doesn’t match an entry in DenyUsers), the login proceeds. If the username is not in AllowUsers or is present in DenyUsers, the connection is terminated with a "Permission denied" error.
The exact levers you control are the usernames. You can specify individual usernames, or use wildcards.
- Individual Users:
AllowUsers alice bob carol - Wildcards:
AllowUsers *@example.com(allows any user from theexample.comdomain)
The order of directives matters. If both AllowUsers and DenyUsers are present, DenyUsers is checked first. If a user is matched by DenyUsers, they are denied regardless of AllowUsers. If they are not matched by DenyUsers, then AllowUsers is checked. If a user is matched by AllowUsers, they are allowed. If they are not matched by AllowUsers (and there are other users specified in AllowUsers), they are denied.
A common mistake is to think these directives prevent any connection attempt. They don’t. The SSH daemon still has to accept the TCP connection and run through the authentication steps before deciding to deny access based on these rules. This means that an attacker can still probe your SSH server, attempt logins, and get feedback on whether usernames exist or if authentication methods are accepted, even if the user is ultimately denied by AllowUsers/DenyUsers.
The next concept you’ll run into is how to actually stop unwanted connections before they even hit the authentication phase, which is where tools like iptables or fail2ban come into play.