Server Security Techniques
- Section 1: Server Security Fundamentals
- Section 2: Intermediate Server-Hardening Techniques
- Section 3: Advanced Server-Hardening Techniques
- Summary
Get general techniques, including several to harden SSH, to secure just about any server you have. Learn how disk encryption can protect data at rest and how to set up a remote syslog server to make it more difficult for an attacker to cover her tracks.
Save 35% off the list price* of the related book or multi-format eBook (EPUB + MOBI + PDF) with discount code ARTICLE.
* See informit.com/terms
If someone is going to compromise your server, the most likely attack will either be through a vulnerability in a web application or other service the server hosts or through SSH. In other chapters, we cover hardening steps for common applications your server may host, so this chapter focuses more on general techniques to secure just about any server you have, whether it’s hosting a website, email, DNS, or something completely different.
This chapter includes a number of different techniques to harden SSH and also covers how to limit the damage an attacker or even a malicious employee can do if she does get access to the server with tools like AppArmor and sudo. We also cover disk encryption to protect data at rest and how to set up a remote syslog server to make it more difficult for an attacker to cover her tracks.
Section 1: Server Security Fundamentals
Before we get into specific hardening techniques, we’ll start with some fundamental server security practices. When attempting to secure your server, it’s important to approach it with the appropriate mindset. With that in mind, there are a few principles you should apply no matter what your server does.
Fundamental Server Security Practices
Fundamental server security practices include the principle of least privilege, keeping it simple, and keeping your servers up to date.
The Principle of Least Privilege
We apply the principle of least privilege throughout the book (such as in Chapter 4, “Network,” when we discuss firewall rules), but in general on a host you want users and applications to have only the privileges they need to do their job and nothing more. For instance, while all developers may have accounts on servers, you may restrict root access to systems administrators. You might also go further and only allow your average developer to have shell access to the development environment and restrict production server access only to systems administrators or other support staff that must have it.
When applied to applications, the principle of least privilege means that your application should only run as root if it is absolutely necessary; otherwise, it should run as some other account on the system. One common reason applications need root privileges is to open a low port (all ports below 1025 require root privileges to open). Web servers provide a good example of this principle in that they must be root to open network ports 80 and 443; however, once those ports are open, the average worker process for the web server runs as a less-privileged user (such as the www-data user). It is common practice these days for web applications themselves to pick some other high port like 3000 or 8080 to listen on so that they can run as a normal user.
Keep It Simple
A simple server is easier to secure than a complicated one. Avoid installing and running extra services (especially network-facing services) that you don’t need. That way, you have fewer applications that can have security holes and fewer applications to keep track of with security patches. It will be easier for your team and for external auditors to validate your configuration if you try to keep files and executables in their standard places and try to stick to default configurations when possible.
Simplicity is also important when designing the overall architecture of your environment. The more complicated your architecture and the more moving parts, the more difficult to understand and the more difficult to secure. If your network diagram looks like a bad plate of spaghetti, you may want to consider simplifying how servers communicate in the environment. Once we get to the network security chapter (Chapter 4) this will make that task simpler as well.
Keep Your Servers Up to Date
New vulnerabilities are found in applications or libraries all the time. One easy way to stay on top of security for your servers is to subscribe to the security mailing list for your distribution and then make sure, as security vulnerabilities are announced, that you prioritize patching servers. The more homogeneous your environment, the easier it will be to keep up with different versions of software, so you will have an easier time if you can stick to a single Linux distribution and a particular version of that distribution. Distribution security mailing lists won’t cover any third-party software you run, however, so you will need to sign up for any security advisory lists those products provide.
SSH Configuration
One of the most common services on just about every server is SSH. While in the past administrators used tools like telnet that sent everything (including passwords!) over plain text, SSH encrypts the communications between you and your server. While that in and of itself is a security improvement, unfortunately it’s not enough. In this section, we go over some basic SSH-hardening techniques you should be employing on all of your servers.
Disable Root Login
One of the simplest things you can do to make your SSH configuration more secure is to disable root logins. While later in this chapter we will talk about how you can avoid password logins to root via the sudo utility (and some systems default to that approach), in this case we are talking about restricting the ability to log in as the root user whether via password, SSH keys, or any other method. Because of how much power the root user has, it’s simply safer to remove the possibility that an attacker could directly log in with root privileges. Instead, have administrators log in as a regular user and then use local tools like sudo to become root.
To disable root login on a server, simply edit the SSH server configuration file (usually found at /etc/ssh/sshd_config config) and change
PermitRootLogin yes
to
PermitRootLogin no
and then restart the SSH service, which, depending on your system, may be one of the following:
$ sudo service ssh restart
$ sudo service sshd restart
Disable Protocol 1
The old SSH Protocol 1 has a number of known security vulnerabilities, so if your distribution doesn’t already disable it you should do so. Locate the Protocol line in /etc/ssh/sshd_config and ensure it says
Protocol 2
If you did have to make a change to the file, be sure to restart the SSH service.
Sudo
In the old days when an administrator needed to do something as root, he either logged in directly as the root user or used a tool like su to become root. This approach had some problems, however. For one, it encouraged users to stay logged in as root. Because root can do pretty much anything you would want to do on the system, mistakes made as root could have much worse consequences than those made as a regular user. Also, from an administrative overhead standpoint, if you have multiple systems administrators who all have root access to a server, you would have to create a shared password that everyone knew. When an administrator inevitably left the company, the remaining team had to scramble to change the passwords for all of the shared accounts.
Sudo helps address many of these security concerns and provides a much stronger security model that helps you stick to the principle of least privilege. With sudo, an administrator can define groups of users who can perform tasks as other users including root. Sudo has a few specific advantages over su:
Each user types his own password, not the password of the privileged account.
This means you no longer have to manage shared passwords for privileged accounts. If a user leaves the company, you only have to disable her account. This also means that administrators don’t need to maintain passwords at all for role accounts (including root) on the system, so users or attackers can’t get privileges they shouldn’t have by guessing a password.
Sudo allows fine-grained access control.
With su, accessing a user’s privileges is an all-or-nothing affair. If I can su to root, I can do anything I want as the root user. While you can certainly create sudo rules that allow the same level of access, you can also restrict users so that they can only run specific commands as root or as another user.
Sudo makes it easier to stay out of privileged accounts.
While you can certainly use sudo to get a complete root shell, the simplest invocations of sudo are just sudo followed by the command you want to run as root. This makes it easy to run privileged commands when you need to, and the rest of the time operate as your regular user.
Sudo provides an audit trail. When a user on the system uses sudo, it looks at what user used sudo, what command was run, and when it was run. It also logs when a user tries to access sudo privileges they don’t have. This provides a nice audit trail an administrator can use later to track down unauthorized access attempts on the system.
Sudo Examples and Best Practices
Sudo, like most access control systems, provides an extensive set of configuration options and methods to group users, roles, and commands. That configuration is usually found in /etc/sudoers although modern systems now often include an /etc/sudoers.d/ directory where one can better organize specific sets of sudo rules into his own files. The sudoers man page (type “man sudoers”) goes into exhaustive detail on how to build your own complex sudo rules, and there are also plenty of other guides available. Instead of rehashing that documentation here, I describe some best practices when it comes to sudo rules and provide a few examples of useful sudo rules along the way. To get started, though, let’s break down a generic sudo command:
root ALL=(ALL) ALL
This command allows the root user to run any command as any user on any system. The first column is the user or group that the sudo rule applies to; in this case, the root user. The second column allows you to specify specific hosts this sudo rule applies to, or ALL if it applies on any host. The next entry in parentheses lists which user or users (separated by commas in the case of more than one user) can run commands—in this case, all users. The final column is a comma-separated list of specific executables on the system that you can run with these elevated privileges. In this case, it’s all commands.
Use visudo to edit /etc/sudoers.
It may be tempting to just fire up your preferred text editor and edit /etc/sudoers directly, but the problem is that if you accidentally introduce a syntax error into /etc/sudoers you could lock yourself out of root access completely! When you use the visudo tool, it performs a syntax validation on the file before it saves it, so you don’t risk writing an invalid file.
Grant access to groups, not specific users.
This is more for ease of administration than specifically for security, but sudo allows you to grant access to a group on the system instead of a specific user. For instance, here are some examples of sudo rules you may have on your system to grant administrators root access:
%admin ALL=(ALL:ALL) ALL
%wheel ALL=(ALL:ALL) ALL
%sudo ALL=(ALL:ALL) ALLEach of these rules is an equivalent to root access. They let you become any user on the system and run any command you want as that user. The admin, wheel, and sudo groups are common groups on the system that a distribution might use to define who can become root.
For a more useful example, let’s say that you administer some tomcat servers and the developers need access to the local tomcat user in the development environment so they can troubleshoot their code. If we had all of their users in a group called developers, for instance, we could add the following rule to /etc/sudoers:
%developers ALL=(tomcat) ALL
Restrict access to specific commands as much as possible.
While it’s certainly easier to just allow someone to run all commands as a user, if we want to follow the principle of least privilege, we want to grant users access to only the privileged commands they need. This is particularly true when granting root access. For instance, if the database administrators (DBAs) needed access to run the psql command as postgres users so they could have more control over system-level database configuration, the lazy way would be to add a rule like the following:
%dbas ALL=(postgres) ALL
The problem is I don’t necessarily want or need the DBAs to do more than run psql, so I could restrict the rule to just the command they need:
%dbas ALL=(postgres) /usr/bin/psql
Always use the full path to scripts.
When writing sudo rules, always make sure to list the complete path to the executable you intend the user to run. Otherwise, if I had just listed psql instead of /usr/bin/psql, a malicious user could create a local script, name it psql, and have it do whatever she wanted.
Write wrapper scripts to restrict risky commands to specific arguments.
In many cases when you write sudo rules, you end up granting more powers than a user really needs. For instance, if I wanted to allow a user to restart the Nginx service, I could grant him access to the service command:
bob ALL=(root) /usr/sbin/service
That would certainly give him the ability to restart Nginx, but he would also be able to start and stop any other service on the system. In this circumstance, it’s better to create a small wrapper script named /usr/local/bin/restart_nginx like the following:
#!/bin/bash
/usr/sbin/service nginx restartThen I would write a sudo rule that just allowed access to that script:
bob ALL=(root) /usr/local/bin/restart_nginx
If I wanted to allow bob to stop and start nginx as well, I could either modify the existing script to accept (and thoroughly validate) input, or I could create two new scripts along the same lines as the restart for the stop and start functions. In the latter case, I would update the sudo rule to be the following:
bob ALL=(root) /usr/local/bin/restart_nginx, /usr/local/bin/stop_nginx, /usr/ local/bin/start_nginx
Make sure that your wrapper scripts are only owned and writable only by root (chmod 775). In general, be careful about executing any scripts that a user can break out of and run shell commands from (such as vi).
Resist writing NOPASSWD sudo rules unless absolutely necessary.
Sudo provides a flag called NOPASSWD that doesn’t require the user to enter a password when executing sudo. This can be a time saver; however, it removes one of the primary protections you have with sudo—namely, that a user has to authenticate herself to the system with her password before sudo allows her to run a command.
That said, there are valid reasons to use the NOPASSWD flag, in particular if you want to execute a command from a role account on the system that may not have a password itself. For instance, you might want to allow the postgres database user to be able to trigger a cron job that runs a special database backup script as root, but the postgres role account doesn’t have a password. In that case, you would add a sudo rule like the following:
postgres ALL=(root) NOPASSWD: /usr/local/bin/backup_databases