Web hosts often shy away from the idea of allowing SSH access to their clients on shared servers, typically due to security concerns bred by the hosting industry. Some of the largest providers, however, are now offering SSH to make their services more functional for advanced users, so now we are often asked whether allowing SSH as a feature is a bad idea. Our answer is, no! If configured properly, there is really no compelling argument that allowing SSH access has any significant negative effect on the security or stability of your server.
Let’s back up a bit, and discuss some of the concerns administrators have over SSH:
- Users can see files that don’t belong to them
- Users can run potentially-dangerous commands
- SSH can be brute-forced
These concerns, while valid, can easily be addressed. I’ll mention that there are hundreds of posts out there that discuss SSH from a security perspective, but many are irrelevant to shared or user-leased platforms due to the fact that they are either difficult to manage or make the use of SSH incredibly complicated for end users. As a hosting provider you should aim to find the proper balance between usability and security – in other words, not making things so secure that they are not usable by reasonable means, nor so accessible that basic security concepts go out the window.
Limiting User Access Scope
While restricted shells such as cPanel’s jailshell may help limit a user’s exposure to the world outside their home folder, there is little the user can see or do via an SSH-obtained shell that they can’t via, say, a PHP or Perl script. Any determined user can simply upload a script to their website that makes a system call using one of the aforementioned interpreters’ built-in functions, and they can essentially run any command they want. For example:
<?php exec('cat /etc/passwd'); ?>
On a side note, hosts that think using disable_functions will effectively deter this need a reality check. This can be easily overridden or bypassed in many environments, and is more likely to render common applications nonfunctional. So after dealing with pissed off customers, you’ll probably end up removing the restriction anyway. If you know for a fact none of your users will rely on the functions you disable, have at it, but understand its limitations.</tangent>
In context to user access, bear in mind the wonder of Linux file permissions. In a standard Linux environment, if a user has files that are readable by others, they will in fact be readable by anyone on the server. Some system files, like /etc/passwd for example, have to be readable to allow a multi-user system to function properly, even if they show a little more information than what you would want users to see. Again though, the ability for users to see these files is not a privilege gained only through having SSH access.
If you truly want to restrict users to their own environments and limit the exposure of certain files, which you should, it’s best to do so at the kernel level – this essentially creates a user-level jail, meaning the user is restricted to their own user space no matter how the they are trying to access files. CageFS provided by CloudLinux and CloakFS provided by BetterLinux both provide this functionality.
Preventing fork bombs
A fork bomb is a type of DoS attack that consists of a command or script that executes (forks) recursive code, causing the system to eventually become unstable. Preventing these consists of setting ulimit values on users to restrict how many processes they can fork at a time. You can enable this through WHM -> Shell Fork Bomb Protection, or using specific ulimit values per user. Do keep in mind that this can occasionally interfere with legitimate processes, most commonly if the user has processes that stay open for long periods of time (IMAP is most common).
Our stance on this is that disabling compilers for under-privileged users serves no meaningful purpose. Denying users access to compilers really doesn’t do much for the security of your server. If anything, it will prevent an attacker from downloading exploit sources and compiling them on your server, but if they can download sources to your server, what’s to prevent them from just compiling the exploit elsewhere and downloading the finished product instead? If your users would have no need to utilize this feature, having it disabled is fine, too.
Securing SSH Access
Any authentication-based service has the ability to be brute-forced, but a compromised shell account comes with the propensity to do more damage in a shorter period of time. There are a few things you can do to help reduce the possibility of a successful compromise here:
Enforcing key authentication
Instead of allowing your users to use passwords to authenticate via SSH, have them use SSH keys. If you’re thinking this is going to be too difficult for your customers to comprehend, consider the fact that most novice users won’t be using SSH anyway. cPanel also has an intuitive interface for managing SSH keys, so your users can handle this aspect themselves through the familiar cPanel interface.
To enforce key authentication, edit /etc/ssh/sshd_config and set:
And restart sshd. Or use the SSH Password Auth Tweak in WHM to change the setting. Before you do this though, make sure your own key is in /root/.ssh/authorized_keys. If you get locked out, remember that you can manage root’s keys via WHM.
Enabling SSH per user
If feasible, you can also disable SSH access by default and enable on a per-user basis. However, again, a user not having shell access doesn’t necessarily prevent them from doing anything nasty on your server. You can manage SSH access via WHM -> Manage Shell Access, or by using chsh. The below example will set user1 to jailshell:
chsh -s user1 /usr/local/cpanel/bin/jailshell
You’ll probably want to force users to use jailshell, which can be set via WHM -> Tweak Settings or in/var/cpanel/cpanel.config as:
The ability for a user to use a shell is configured by the package of the user account, which is set up under the WHM -> Packages section, so you can enable or disable shell access based on what package the user is assigned. If the user’s package has shell access enabled and you do not have jailshell set by default, the user’s shell will default to /bin/bash.
Disabling user switching
It’s strongly advised to not allow untrusted users to switch to other users. The ability to switch users is granted by thesu command, and by default on cPanel servers this is restricted to those belonging to the ‘wheel’ group. If it’s not, you can set it this way as follows:
chmod 4750 /bin/su
If a user needs to be able to switch to another user, they have to be added to the wheel group first, and the change will take effect on their next login. To add a user to the wheel group, you can either go to WHM -> Manage Wheel Group Users, or run:
useradd -G wheel user1
Running sshd on a second port
Changing your SSH port alone does very little to improve security. At best, it can help against automated large-scale brute-force attacks that tend to target port 22, but if your SSH port is open, anyone that knows how to use nmap can find it. What a lot of major hosting providers do is use two ports – one for internal use, and one for customers. So basically you’d have something like:
- Port 22: Internal use
- Port 23: Customer use
In the above scenario, you’d firewall off port 22 and only allow your IPs, then keep port 23 open for customers. There are two reasons for this:
- In the event of an attack against port 23 that somehow renders it inaccessible, you can still access SSH over port 22 to manage your server
- If an issue arises to where you need to disable customer SSH access, you can simply block port 23 or shut off the SSH daemon listening on it, while allowing SSH access for yourself on port 22
For information on running sshd on multiple ports, see this post. It’s a slightly older write-up, but the information provided is still accurate. (Note: in the post where the reference to iptables is being made, if you run an iptables frontend like APF or CSF, make sure you allow the port through those interfaces instead, as any custom iptables rules you add will be overwritten)
Firewalling your SSH port(s)
Typically, any ports on your system that should not be accessibly by the public should be blocked off by a hardware or software firewall. If you’re using two SSH ports as described above, your internal port should be firewalled off to only allow access from trusted locations. For your customer port (whether it’s the same or different from your internal one), the ease of blocking it depends on how many users need to access it. If you’ve done everything else described in this post, there’s little harm to leaving it open. If you want to block access to it, keep in mind you’ll need to manually add your users’ IPs, (which may change, depending on their ISP) and this could be a tedious setup to maintain if their IPs change, you frequently move users around to different servers, or if users cancel. If you do this, consider ensuring that you’re only whitelisting their IP(s) for access to the SSH port rather than ever port on the server.