Practical Guide to Software Quality Management (Artech House Computing Library)
< Day Day Up > |
As the BSD kernels have evolved, many of their internal data structures have become more exposed to the administrator. Modern BSD kernels can now be tuned while they run, using a program called sysctl(8) that displays and sometimes alters the value of a variable in the running kernel. Most sysctl variables control highly specialized and localized functionality in the kernel. Your server's role, and the environment in which it deployed, largely determine which of these variables if any you need to set. A very active database server might need more open files per process than usual. A very active web server will often need various TCP/IP variables tuned. An Internet server that is in a hostile network will need options set that are unnecessary for an intranet server on a friendly network. These variables might indicate the default settings in a kernel module or device driver (e.g., net.inet.ip.ttl=64). Sometimes they offer low-level tuning on things like buffer sizes (e.g., kern.ipc.maxsockbuf=262144). Some of them are read-only and dynamic to provide a snapshot in time of some kernel state that is constantly in flux (e.g., vm.loadavg=0.04 0.08 0.08). Others merely offer a convenient way to get information out of the kernel. Changing these values is a fundamental skill that will be required as part of most significant tasks discussed in this book.
2.2.1. Setting sysctl Values
Reading sysctl values is easy: just run sysctl variablename. Any user can read all kernel values. Setting variables is equally easy. Just add an equals (=) sign and the new value: sysctl kern.securelevel=2. Only root can set values. Once you have a set of sysctl values that you like and want to make default, you can add them to sysctl.conf(5) in a simple variable=value format. The only values that can go in /etc/sysctl.conf, however, are those that can be set once the system is up and running multiuser. FreeBSD allows some values to be set at boot time, but not later. Those go in /boot/loader.conf. The loader(8) manpage lists which variables can only be tuned at boot time in this file.
2.2.2. Kernel Security Level
There are several sysctl variables that are important to overall system security. Probably the single most important sysctl variable in the entire system is the variable kern.securelevel, simply referred to as its securelevel. Its value has diverse effects across a wide variety of functions and features. Table 2-2 summarizes the various ways in which the kernel securelevel affects system operations. They are explained in detail in the following sections.
* Raw disk devices only exist in OpenBSD. Root can raise the securelevel at any time, but it can never be lowered. You must change your configuration file and reboot to run at a lower level. 2.2.2.1 Level -1: "permanently insecure"
If the system finds itself in securelevel -1 at the end of the boot process, it will not raise the securelevel. Thus, setting your default level to -1 is how you get the system to stay insecure at boot time. At various times in later chapters we will recommend that you "reboot to a lower securelevel" in order to accomplish something that can only be done that way. Setting your default securelevel to -1 is how you'd do that.
At kernel securelevels 0 and -1 the operating system behaves as like any traditional Unix: root is supreme and can do all things. All filesystem flags are enforced but can be both set and unset. All devices can be read and written to as their permissions indicate. Firewall rules can be set and unset at will, and the system clock can be set arbitrarily. Production systems that have any sort of security requirement should normally run with a securelevel higher than 0. It's worth noting that even if you immediately promote your system to securelevel 1, you'll still be able to configure everything without difficulty. It's only after flags have been set on something you want to change that the securelevel may interfere. You can install new packages and set flags on the newly installed files without worrying about the securelevel. 2.2.2.2 Level 0: transitional security level
There is no operational difference between secure level 0 and securelevel -1. The system only runs in level 0 briefly during boot time and when it is in single-user mode. Generally systems boot at level 0 and then switch to a higher level after booting, or they boot at -1 and stay there. Securelevel 0 is unusual in that you can lower the securelevel back to -1 if you want to. If your OpenBSD system normally runs at securelevel 1 or 2 and you want to boot to a lower level just once, you can do so without modifying your configuration file. Boot to single-user mode and you will find your system in securelevel 0. Set the securelevel to -1 by running sysctl kern.securelevel=-1. When the system boots, it will not increase the securelevel to its default. 2.2.2.3 Level 1: improved operational security
At level 1, the kernel imposes stricter security constraints than a traditional Unix system. Write access to raw disk devices (which exist on OpenBSD and FreeBSD 4.x) is denied, even to root-equivalent processes, if the raw device corresponds to a currently mounted filesystem. On FreeBSD, though raw devices don't exist, similar constraints apply. You cannot write to the disk device of the mounted filesystem.
The kernel also enforces the system append-only and system immutable flags (see Section 2.1, earlier in the chapter). The /dev/mem and /dev/kmem devices cannot be opened for writing, which helps protect against rogue processes writing into other processes' memory. Kernel modules cannot be loaded or unloaded in any level greater than zero, and this might occasionally interfere with your maintenance. The BSD kernels have become increasingly modular in recent years, which helps reduce the amount of RAM the kernel uses. They don't load device drivers for devices you don't have. For example, most dedicated servers rarely mount CD-ROMs. The ISO-9660 filesystem driver, therefore, is not built into the kernel. It is available instead as a kernel module. The first time you try to use a CD-ROM on a system running at securelevel 1 on higher, the mount(8) command will automatically try to load the kernel module and fail. If the ISO-9660 driver is something you need often, you will have to either add a command to the boot process to load the driver at boot time, or compile the driver into the kernel. For dedicated servers, this is rarely an issue after the server is configured the first time. Just be aware that you cannot add certain module-based functionality to a server with a non-zero securelevel. You have to reconfigure the system so it will boot into a lower securelevel, reboot, and then do the work that requires the kernel module. 2.2.2.4 Level 2: high security
Each level of security includes all the protections of all the lower levels. At level 2, all of the level 1 protections remain, but several are expanded. For OpenBSD and FreeBSD 4.x, no raw disk device can be opened for writing at all once you're in level 2. This means, among other things, that the newfs(8) command cannot be used to create filesystems. Additionally the growfs(8) command will not run at this level. Even though FreeBSD does not use raw devices anymore, these same restrictions are implemented in FreeBSD on the so-called "cooked" devices.
This protection is selective, so devices such as tapes and network devices are still accessible to processes with root privileges. Additionally, the protections are only for writing to raw disk devices, not reading. If reading from raw devices were restricted, then dump(8) and fsck(8) would not be able to run. Programs like FreeBSD's camcontrol(8) and anything else that tries to directly manipulate the SCSI bus would fail, though. Interestingly mtx(1), a tape library control program available in ports/misc/mtx, will not work at this securelevel, but the built-in chio(1) library control program will. The reason is that mtx tries to use /dev/pass0, a device that directly reads and writes on the SCSI bus, whereas chio uses API calls in the operating system. If direct reads and writes to the SCSI bus were allowed, then disks could be written and manipulated bypassing the securelevel. Time is also carefully controlled at level 2 and higher. It cannot be adjusted forward or backward by more than 1 second at a time. This restriction on time has two beneficial effects. It makes log entries more trustworthy because the time cannot be modified to make events look like they happened in the future or in the past. Accurate time also affects digital signatures. If any processes on the system use asymmetric cryptography to apply digital signatures (e.g., PGP or S/MIME email), the accuracy of the clock is critical to the validity of the signature. Restricting time adjustments is not normally a problem when you keep your time synchronized using ntpd(8). At boot time, before the securelevel is set, the system calls ntpdate(8) (or ntpd -q) to adjust the clock however much it needs to be adjusted. Then the system starts the ntpd daemon, to continuously synchronize the local clock with the public time servers. Although your typical Intel-based PC system clocks tend to drift significantly, even the worst can be kept in check by ntpd. 2.2.2.5 Level 3: network security
This level only exists in FreeBSD. The protections it offers in FreeBSD are duplicated in OpenBSD at securelevel 2. At kernel securelevel 3, two network features the firewall rules ipfw(8) and ipfirewall(4) as well as the dummynet(4) traffic shaping parameters become immutable. This is most beneficial when the system is acting as router, performing network address translation, or is some other core network device. It helps prevent an attacker from opening holes in your firewall to allow malicious network traffic through. These are the only differences between level 3 and level 2. If you do not have concerns about firewall rules (for instance, if you do not use the firewalling features), there is no particular value in running in securelevel 3. 2.2.2.6 Setting the securelevel for FreeBSD
The FreeBSD securelevel is controlled by two variables in /etc/rc.conf(5). A typical configuration looks like: kern_securelevel_enable="YES" kern_securelevel="2" By default, kern_securelevel_enable is set to NO in /etc/defaults/rc.conf, which causes the system default of 0 to be demoted to -1 at boot. You may find additional details about securelevels in the manpage for init(8). 2.2.2.7 Setting the securelevel for OpenBSD
The OpenBSD securelevel is configured to be 1 by default. To change your default securelevel, edit /etc/rc.securelevel(8) and change the line securelevel=1 to a new value. OpenBSD documents its behavior in the securelevel(7) manpage. 2.2.2.8 Thoughts on using securelevel
Making rc.conf or rc.conf.local immutable in a securelevel greater than 1 is the only way to prevent an attacker who gains control of your system from lowering its security level. The only way to reduce the security level of your system in such a configuration is to interrupt the boot process at the console and enter single-user mode. Before trying to "make your system go to eleven" on the kernel securelevel, however, think about whether the hassle in maintenance is worth the additional security. We would argue that the additional security is mostly illusory, but the maintenance hassle is absolutely real. The additional security is minimal because your attacker's goal is probably not to reboot the system to a lower securelevel. Instead, she really wants to modify database records, set up back doors into the system, and so forth. If she gets into the system at all, you will need more than directory permissions and securelevel to protect your assets. Don't look at securelevel as a silver bullet that cures all security problems. Security levels can make administration more complicated and time-consuming in addition to making systems safer. Sometimes all the work of raising the securelevel can be bypassed somewhat, too. See the sidebar "Sidestepping Immutability," for an example of bypassing securelevel in FreeBSD 4.x or OpenBSD. The more volatile your filesystem, the harder it is to run at a high security level all the time. Each time you install software, you will be installing files in potentially sensitive areas. For instance, if you use something like Osiris (http://osiris.shmoo.com/) to capture all the attributes of files on the filesystem, that database will need to be updated after a software install. If that database is stored on the system itself, it will probably be immutable, and the securelevel settings will make it impossible to update it without rebooting into a lower securelevel. Clearly software installation in such an environment requires a deeper depth of planning and staging than a non-securelevel installation. The more sensitive your users are to system reboots, the more planning and staging you'll need. The above considerations notwithstanding, no server that needs to be secured should omit the kernel securelevel setting. It is one of the distinguishing features that sets FreeBSD and OpenBSD apart from similar free Unix-like operating systems. 2.2.3. Other Security-Related Kernel Variables
Several additional variables are available that each add some small value to securing a server. Not all of them are appropriate to all environments. 2.2.3.1 Random PIDs
A number of exploits, such as race conditions, make use of the fact that process IDs (PIDs) have historically issued sequentially by the operating system. Process ID 1 is assigned to init, and then all the others are issued incrementally. If the server launches the BIND nameserver at boot time, for example, the named(8) process probably gets the same PID (plus or minus a few) every time. Unless something unusual happens and the nameserver is killed, an attacker can count on named having that PID on his target. Of course, every server will be slightly different, but once you learn PIDs for a particular system, they will only change a little each time the server boots. Other processes, such as sendmail(8), fork often. If a process runs with root privileges and it forks, there is a second copy of it (the child process) running with root privileges for some small instant of time. Typically the child sheds its root privileges as quickly as it can, but sometimes the child process is vulnerable to some outside influence like creating a temporary file named the same as one it plans to open. By knowing the PID of the child process and how to influence it, attackers can try to exploit these kinds of race conditions. FreeBSD allows you to choose to assign PIDs to processes randomly by setting the kern.randompid variable to 1. PID 1 still goes to init, but everything else is random. OpenBSD does not offer a choice for this behavior. It always assigns random PIDs, except to init. 2.2.3.2 Controlling core dumps
When a process crashes, or when it is sent certain unhandled signals, it might "dump core" in an attempt to aid in debugging what went wrong. More often than not, this core file is large and useless for the system administrator. It is a snapshot of the memory the program was using at the time the program crashed, along with various register values like the stack pointer. If you are the program's developer and you have the source code and motivation, you can use the core file to help track down just what was happening when the program crashed. Most of the time, system administrators and system users have no use for core files. More importantly, such files can inadvertently leak important information to a malicious user. Imagine, for instance, a situation where an attacker can cause the web server process to dump core. Perhaps there is a buffer overflow that causes a segmentation fault, or some kind of unhandled exception. If the web server dumps core while it has, for example, the server's SSL private key unencrypted in memory, that key will be in the core file. While the core file probably will not be dumped somewhere visible in the web hierarchy, the attacker may have some other means of getting the httpd.core file from the web server (e.g., perhaps the attacker has a login account). If he does get the core file, he can rummage through the core file to extract the SSL private key, and now he can decrypt all that server's traffic! A lot of things have to go wrong for such an attack to actually succeed, but you can see the potential. Table 2-3 describes some sysctl variables that control core dump behavior. In general, core dumps should be turned off unless you have a very specific need for them. Note that two of these are FreeBSD specific, and the last one performs the same role, but is named differently in FreeBSD and OpenBSD.
2.2.3.3 Reducing visibility in the network
Two related variables tweak behaviors in the TCP/IP stack to make a system less visible to probes. This helps you keep a low profile in the presence of automated scanners, and it helps reduce the amount of resources the kernel spends responding to such probes. The variables are net.inet.tcp.blackhole and net.inet.udp.blackhole. To use them, put the following lines in /etc/sysctl.conf: net.inet.tcp.blackhole=2 net.inet.udp.blackhole=1
These variables cause the kernel to drop packets when another system attempts to connect to a TCP or UDP port where no process is listening. The normal kernel behavior would be to compose a TCP reset packet, or an ICMP port unreachable message, and send it as a response. When the two "blackhole" sysctl variables are set, the kernel does nothing when it receives connection attempts on non-listening ports. The probing system gains no information about your system. It cannot distinguish a non-listening port from a timeout in the network. This will slow an attacker down some because he will wait some amount of time after each probe packet is sent. His scan will take a lot longer. Turning on these variables also reduces the amount of resources consumed by the kernel's responding to such probes. Any system that is subject to a lot of random port scans or probes should have these variables on. Any system that is connected to the Internet is subject to just such scans and so should have these variables enabled. 2.2.3.4 Dropping "synfins"
A similar variable net.inet.tcp.drop_synfin will cause the kernel to drop all TCP packets that have the SYN and FIN bits set.
These so-called "synfin packets" are often used by programs like queso and nmap to "fingerprint" an operating system. Some believe that dropping such packets (i.e., not responding to them) violates the TCP specification, but there are vigorous compelling arguments on both sides. Some system and network administrators are not comfortable with dropping them. It is unlikely, however, that failing to respond will have any adverse effect on the operation of your server. Dropping SYN+FIN packets requires that you change your FreeBSD kernel configuration. Unless you add the option TCP_DROP_SYNFIN statement to your configuration and recompile your kernel, the net.inet.tcp.drop_synfin variable will not be honored. Consult Chapter 9 of the FreeBSD Handbook (O'Reilly) for a thorough walkthrough of kernel configuration and compilation. |
< Day Day Up > |