![]() |
| August 2001 | Get BSD | New to BSD? | Search BSD | Submit News | FAQ | Contact Us | Join Us |
|
Chapter One
Greetings, fellow FreeBSD users, lets begin.
First, start with a typical install. If you are not familiar with this process, you should be reading the handbook on installation before consulting this article.
In this chapter we'll talk about the lockdown procedures of a freebsd machine. This article assumes the end user has a general level of familarity with FreeBSD, and unix, in particular, file permissions, kernel configuration, file editing, and basic ssh usage.
In the second chapter we'll go depth with suid bits, file attributes and daemon configuration. In the 3rd chapter we'll talk about OpenSSL, IPSec and encryption in general, including GPG/PGP and TCFS.
First off install nmap. It's a very useful tool, and we'll be using it a bit later on to check on security and on packet filtering.
In order to install nmap, you can use the ports, or if you wish, simply use the packages.
# pkg_add -r nmap
Let me explain: nmap is a portscanning util. You can use it throughout this article as you progress through the various steps to check on your machines remote security. While nmap is an excellent tool, it does not give you a picture of local security. nmapping your servers every so often, even after they are locked down, is usually a good idea. If only to check if additional services have been added or to see if firewall rules have been modified.
Another tool I would strongly suggest is Nessus. Nessus is an excellent method for checking remote host security, and is in fact used by many professional auditors for that very purpose. Nessus is also in the ports collection and it can be found under "security/nessus".
Next lets move to one of the most important areas of security: logging. While FreeBSD is one of the most secure operating systems around, it does have some drawbacks in the area of logging. By default things don't really log to very useful places.
Lets edit /etc/syslog.conf and make some changes.
I usually modify my security.* /var/log/security line to read like this.
security.*;auth.info /var/log/security
The reasoning behind this is simple, by default ssh logins to the logging facility auth. You can, if you like, make this a separate log file using the following in your syslog.conf instead of the above.
auth.info /var/log/auth
If you prefer, you can create another file. However, if you do this, make sure you update /etc/newsyslog.conf to include it, for example:
/var/log/auth 600 10 100 * Z
It's also a good idea to modify newsyslog.conf to fix some of the world readable permissions on some log files: for example maillog and messages log. I usually change these to 600, so that they are not readable by the world. I really don't need to have them world readable, so I modify the 644 value to 600 in the newsyslog.conf file.
/var/log/messages 600 5 100 * Z /var/log/maillog 600 7 * @T00 Z
Let us return briefly to syslog. I do not like how important data is written to console, since I plan on managing servers remotely, with respect to desktops I plan on using the console to edit system files. The log reporting to console I find is very distracting when modifying system files or doing any sort of programming. So here's how to prevent it. Comment out these lines.
# *.err;kern.debug;auth.notice;mail.crit /dev/console # *.err root # *.notice;news.err root # *.alert root
In the case of workstations or desktops, you might actually want to read information easily. Please note the following is probably not a good idea for servers or publically accessible machines as it could lead to a compromise of information.
*.* /dev/ttyvb
The above will cause all logged information to be dumped onto virtual terminal B, which is accessible via ALT-F12. It's very useful for debugging purposes, and it's the setting I use on my workstation/sandbox machine. If you do this, however, you may want to look at using "security/vlock" to lock your console when you are away from the keyboard.
Finally, to prevent users from reading these files, you'll want to issue the following commands. The reasoning is you dont want to give people any additional information, that they might be able to use against you.
# chmod 600 /etc/syslog.conf # chmod 600 /etc/newsyslog.conf
Now, I think we have logging pretty much covered. (Make sure to test it out fully on your own machine.) We are now ready to move onto SSH itself. Since SSH will be your primary remote administrator tool and one of the few services running, you should pay special attention when locking it down. SSH itself can lead to a security breach, as some of us have seen.
The sshd control file is /etc/ssh/sshd_config. If your not planning on accessing your server or box remotely with anything using SSH protocol 1, then I would recommend disabling it completely. It's generally not considered as secure as protocol 2, and in any event, you should not be offering any services that you do not expressly need. This will also stop someone from hijacking the startup session and downgrading you to protocol 1 by modifying the packet carrying the version banner. This could, in theory, be done to force communication using the weaker ssh 1 protocol.
Uncomment the Protocol 2,1 line and change it to read
Protocol 2
Secondly, SSH takes up a lot of memory when it's being run and it's a commonly dos'ed service. Since each connection takes up a good chunk of memory, FreeBSD by default has a line called "MaxStartups". Personally I think the defaults are rather high, and unless you're running a shell box, the values can be lowered greatly.
MaxStartups 5:50:10
This seems far more reasonable unless you're going to have a lot of people administrating this system or you're offering shell services. Even then, this is a rather healthy value for most shell providers. Remember MaxStartups does not mean the total number of connections, only the total number of not-yet-authenticated ones. This means that 5 people can be in the process of logging on at any given point in time. On a sealed box, this is more then enough.
Fortunately, by default, FreeBSD's OpenSSH configuration disables both remote root logins and empty passwords. I'd also suggest you disable X11Forwarding by changing the X11Forwarding line to read:
X11Forwarding no
If you are running a server, the server should not have X installed to begin with, so there's no good reason to have X11Forwarding set to yes in a server configuration. The reasons for turning off X11Forwarding are simple; with X11Forwarding on, a compromised remote server will be able to send processes that can directly attach itself to your X11 session. That means it can log keystrokes, display nasty messages, and capture your display.
Obviously users can still set up X11 sessions if they use simple port forwarding, or worse, set up unencrypted connections. That, however, is difficult to stop. At least this setting will prevent the more clueless users from doing it. Also, this will give the more knowledgable ones the hint that you don't want X11 sessions going on between machines.
I also strongly suggest you move away from static passwords completely and use DSA or RSA keys. You can disable password authentication by doing the following. DSA keys are generally considered more secure, but either is fine in my opinion.
PasswordAuthentication no
I will also be writing up a section on how to use DSA or RSA keys. However, at this moment I should point out that password authentication is usually not a very secure method regardless of the protocols, cause it can be socially engineered, guessed, stolen from another source or beaten out of an employee.
Also regarding sshd, if you're running a server that's going to have a lot of accounts on it, most or many of these accounts probably will not have shell access. I'd recommend doing the following, if nothing else.
AllowGroups shellusers or AllowGroups wheel
Or better still, if the number of people with shell access is relatively small you can do the following.
AllowUsers aeonflux
Or whatever your user name is. Allowing only that specific user, or users to access the machine via sshd is usually a very good idea, so you dont accidently end up giving people shell access to your machine, or allow them to setup port forwarding using ssh.
As I'm sure you're aware. If you dont want users to have shell access, you should specify /sbin/nologin as their shell. This can be done using chsh.
chsh -s /sbin/nologin user
Lastly, regarding sshd and security, sshd in FreeBSD is compiled with tcpwrappers support. While tcpwrappers are a poor replacement for proper packet filtering, I'm of the opinion that a layered security stance is best. Packet filtering can fail, tcpwrappers can fail, however if stacked one on top of the other, the whole is less likely to fail.
Open up /etc/hosts.allow
By default you'll need to remove the line "ALL : ALL : allow", as well as some of the other examples. I recommend starting off with a completely blank and fresh file, unless you've already modified your /etc/hosts.allow file, in which case add the following:
sshd : localhost : allow sshd : friendlycomputer : allow sshd : all : deny
friendlycomputer is the IP address of the machine you wish to log in remotely from. I suggest against using hostnames in hosts.allow, since DNS can be compromised, spoofed or tampered with at another source, which ends up making your security dependent on another machines.
That ends our section on sshd. Lets move on to the client part. Open up /etc/ssh/ssh_config and change it to read as follows.
Host * ForwardAgent no ForwardX11 no Port 22 Protocol 2
Basically, make sure ForwardAgent and ForwardX11 are turned off, and make sure it's using Protocol 2. If you want to connect to Protocol 1 daemons, add them in to your ~/.ssh/ssh_config configurations seperately.
As a side note, this is the default behavior for more recent versions of OpenSSH in FreeBSD. However it is not in other operating systems, or in older versions of OpenSSH. It's best to get in the habit of explicitly declaring these things in the configuration files, in case the default behavior is changed at a later date. However, as it stands, it's not necessary to perform this step, although personally I do anyway.
If you'd like your failed ssh logins to show up in the nightly security email, as they probably should, you'll need to apply this patch to your /etc/security. By default, FreeBSD doesn't audit failed ssh logins. At the moment this patch only checks for failed password logins. I'm going to add failed dsa/rsa key login attempts and illegal users next. Hopefully the final patch will become part of the base install.
--- /etc/security Mon Jun 11 15:45:02 2001
+++ /etc/security Mon Jun 11 15:48:29 2001
@@ -44,6 +44,7 @@
sort -t. -r -n +1 -2 |
xargs zcat -f
[ -f $LOG/messages ] && cat $LOG/messages
+ [ -f $LOG/security ] && cat $LOG/security
}
sflag=FALSE ignore=
@@ -188,6 +189,12 @@
separator
echo "${host} login failures:"
n=$(catmsgs | grep -i "^$yesterday.*login failure" | tee /dev/stderr | wc -l)
+[ $n -gt 0 -a $rc -lt 1 ] && rc=1
+
+# Show "${host} SSH login failures:"
+separator
+echo "${host} login failures:"
+n=$(catmsgs | grep -i "^$yesterday.*failed password" | tee /dev/stderr | wc -l)
[ $n -gt 0 -a $rc -lt 1 ] && rc=1
# Show tcp_wrapper warning messages
We now need to move on down the OSI ladder a little, to networking. By default FreeBSD and most operating systems send a RST packet when they receive data close ports. SYN or no, they build a new whole packet and send it off, telling an attacker that the port is closed and they should continue scanning the next highest port. Usually we dont want to make portscanning easier, nor do we want to waste valuable CPU cycles in the effect of a DoS attack. So we're going to use a little feature in FreeBSD called blackhole. Consult the man page blackhole(4) if you want further information on how this works. If you are using restrict_rst I'd suggest switching over to blackholing as well. It's more effective, and restrict_rst will be removed by release 4.4, as it's already been removed from -stable.
To implement blackholing, do the following. Open up /etc/sysctl.conf and add the following lines.
net.inet.tcp.blackhole=2 net.inet.udp.blackhole=1
This will set the MIBs when you boot up. To activate the changes perform the following command.
# /bin/sh /etc/rc.sysctl
While this isn't a replacement for packet filtering, it is an additional layer of security. I use it in combination with ipfw to catch anything that falls through the ruleset, or to give me some protection when I've flushed all the rules from some reason. There is another added benefit; if you have to punch holes in your firewall to allow ftp-data, dcc requests or realplayer streaming, you wont be left completely unprotected since any data that slips through the ruleset will be blackholed anyway. It would, however, be nice if ipfw handled related states a little better.
Let's move on to basic services. You should have a pretty good idea of what services you must run and the ones you don't need. When in doubt, disable it, you can always re-enable it later if something/someone needs it. Security needs overrule productivity sometimes, and this is one of them.
First up, open up /etc/rc.conf
Unless you have a good reason to run portmap, such as NFS, NIS and so on, I'd recommend disabling it. People often scan entire network blocks looking for port 111 to see if there are any vulnerable rpc services they can exploit. If you need to run portmap I'd recommend dropping dstport 111 at your border router.
portmap_enable="NO"
Unless you're running a mail server, or mail gateway, I'd recommend putting sendmail into queueing only mode. If you need to run an actual smtp gateway I'd recommend installing postfix.
sendmail_flags="-q1m"
I'd also suggest you drop icmp redirect, which can be used to DoS or hijack sessions. Don't enable it unless you have a specific reason to do so. Even then, it's sometimes best to let packets travel the less optimal way if it benefits security.
icmp_drop_redirect="YES"
You can also log icmp redirect, however I'd suggest against it because many times Cisco routers will try to send icmp redirects for legit reasons and not as a hack attempt. Depending on your situation you can enable dropped redirect packets to be logged. Personally I do not, as packet filtering takes care of this anyway.
icmp_log_redirect="YES"
For those of you who are curious you can disable that behavior on Cisco routers by doing "no ip send-redirects" on the ethernet interface.
You can also have your machine drop SYNFIN packets. This does break the TCP specification and there are better ways to do this with IPFW (more on that later). However, if you want to do it, add this to your /etc/rc.conf
tcp_drop_synfin="YES"
Also if you want this behavior to work you'll need to add the following to your kernel configuration.
options TCP_DROP_SYNFIN
Finally you'll want to chmod rc.sysctl, rc.conf and sysctl.conf to prevent your users from reading them.
# chmod 600 /etc/rc.sysctl # chmod 600 /etc/rc.conf # chmod 600 /etc/sysctl.conf
Next lets talk about crontab. I'm very careful which users you allow to use crontab, since it's a very powerful tool to give someone. Also, there have been many exploits in cron. It can be used by an end user to setup bots, waste resources or just annoy people. In the past, script kiddies have been known to exploit crontab to gain root access on machines. Many times this is possible through a badly designed cgi or server script. The users 'www', 'nobody' and 'bind' should definitely be denied from creating crontabs.
Create a file /var/cron/allow and put inside a list of the user you want to be able to use cron. For most situations, just allowing cron to use crontabs is acceptable. Depending on your individual needs, I suggest just doing the following. In most cases it's better to create an exclusive list, rather then creating a list of users you don't want to create crontabs.
# echo root > /var/cron/allow # chmod 600 /var/cron/allow
While we're at it, we may want to prevent /etc/crontab from being read globally. Usually this stays unmodified on a system, but there are a few cases when you may want to alter it. Regardless, there's no reason to allowing users to read it. So perform the following.
# chmod 600 /etc/crontab
Btw, if you dont use 'at' (which most people do not), then go ahead and disable it too. Remember the security motto "If you dont use it, lose it". Nature already figured this one out long ago, but we engineers aren't quite as smart. Comment out the /usr/libexec/atrun command in the /etc/crontab file.
# */5 * * * * root /usr/libexec/atrun
inetd is also on by default. inetd usually controls alot of legacy and therefore insecure services such as telnet, ntalk and finger. There are more modern and superior replacements for inetd. However FreeBSD's inetd is actually one of the better ones around. Still, unless you need any of these services, I'd suggest disabling it completely. Check /etc/inetd.conf to see if any of these services are needed by you. FTP, for example, can also be run in daemon mode, and doesn't require inetd.
To disable inetd, modify the following in your /etc/rc.conf
inetd_enable="NO"
If you're going to use FreeBSD's inetd, you should be aware that it has built rate limiting ability. You can control the rate at which ident requests, for example, are processed by doing the following:
auth stream tcp nowait/10/10 root internal auth -r -f -n -o UNKNOWN -t 30
The first 10 is the number of maxchild processes we're going to allow. The second value is the max connections per ip per minute. For ident a value of 10/10 is fine. Unless your box is a major SMTP gateway, there is no reason it should need to process more then 10 ident requests at one time. Even then, dropping ident requests doesn't stop the flow of mail. In fact, the only good reason to be running ident these days seems to be irc. If you don't irc, I'd recommend disabling the service entirely.
Rate limiting of this sort will also work for other services as well. Consult the inetd man page if you want to know about how this works.
Make sure your system's clock is set correctly. If your time is incorrect, your log files will be basically worthless. Now your time doesn't need to be super dead on, but within a few minutes is usually sufficient.
By default, freebsd comes installed with ntpdate.
# date
Make sure you're in the right timezone.
# ntpdate ntp.nasa.gov
Your date should now be set correctly, using the nasa ntp server, which is from my understanding pretty accurate.
At a later date you can perform the command again and see how much clock drift you have. Usually a few seconds isn't important. You may also want to make it a cron job, in which case ntpdate should be called with the -s flag in other to divert the output to syslog.
It also be noted that your time doesn't need to be exactly dead on to the second. Odds are everyone else's won't be either. So in pratical terms, I'd say within 5 minutes is sufficient.
Since we're on the subject of time, dont you find those time marks in syslog to be particularly annoying? Want to remove them?
syslogd_flags="-ss -m 0"
Added to your /etc/rc.conf, oh and the double -s also has the nice effect of closing the udp port for syslog completely. Unless you're using syslog for logging over the network, I'd suggest setting this.
We could talk about securelevels for an entire article, but let me boil it down to basic terms. Workstations should run at securelevel 0, since they'll probably want to run X11, and servers should probably run in 2 or 1. Depending on how often you can reboot the machine to single user mode to make major adjustments. It should also be noted that securelevels can be defeated rather easily by an intelligent cracker, but every additional layer of security helps.
Personally, I run servers at 2, and my laptop and workstations usually run at 0. In /etc/rc.conf modify the following
kern_securelevel_enable="YES" kern_securelevel="2"
Please consult man init(8), or man securelevel for more information about these. Understand exactly what each level means before implementing it.
You can reboot for these settings to take effect. Or you can just perform:
# sysctl -w kern.securelevel=2
Local security is very important; unfortunately, it's something that few open source operating systems really pay proper attention to. If your machine is located at a colocation host, or worse, in a data center where MCSEs might be wondering about, it usually a good idea to give it some protection.
Now obviously, no form of local security is 100 percent effective. If someone wants to wreck your machine and has physical access to it, you're probably in big trouble (think hammer, bomb, or water). However, you can make it rather difficult for them to get at your data or take down your server. Many times, I've seen MCSE's who've hit ctrl-alt-del on a freebsd box thinking it was how the login was called.
Lets begin with /etc/ttys. Open it up in your favorite editor and find the console line:
console none unknown off secure
Change "secure" to "insecure", so the user is asked for the root password when going to single user mode. Be warned this will also make recovering lost root passwords more difficult, But it will prevent someone from gaining root access to your machine locally provided they do not have a boot disk.
This leads us to our next topic for discussion. Modify the BIOS so that the first and/or only boot device is the hard drive. That way, even if a CDROM or boot disk is stuck into the physical machine, FreeBSD will be loaded off the hard drive rather then any inserted media. It also goes without saying that you should password the BIOS so that a password is required to make changes (but not to boot up, because you want your server to be able to reboot without a person standing in front of it). Most modern BIOS types support this behavior.
Also, locking the case, or the whole rack, is a good idea too. However this level of physical security is beyond the scope of this document.
Next, we need to talk about virtual terminals and virtual terminal buffers. Personally I find this an area of great concern, as the virtual terminal buffer does not flush after you logoff. In other words, all of the activity you did locally in front of the machine can be reviewed if someone walks up to your machine and hits scroll lock.
There is however a solution to this on servers, and on workstations that primarily use X. You can disable the virtual console buffer altogether. To do this you'll have to edit your kernel configuration file.
Add the following lines to your kernel configuration.
SC_NO_HISTORY # This option disables back-scrolling in virtual terminals
While you're editing your kernel configuration, you may want to add a few other options relating to syscons. For more information see syscons(4).
SC_DISABLE_DDBKEY # This option disables the debug key. SC_DISABLE_REBOOT # This option disables the clt-alt-del key.
Finally, if you want login prompt to appear on a blank screen, add "clear" to your /etc/csh.logout, /etc/bash_logout, or other logout file, depending on your shell.
Even though we've already enabled blackholing, it should be noted that it's not a really great alternative to proper packet filtering. So lets talk about FreeBSD's packet filtering suite.
To use ipfw, you can either compile support into your kernel or just use the kernel modules. If you setup the rc scripts properly, the kernel module will be loaded for you when you boot up.
First let's create our own firewall rules. Create a file /etc/firewall.rules and list inside of it your own rule set. I'd recommend you chmod 600 the file too.
ipfw -q -f flush ipfw -q add 00100 allow ip from any to any via lo0 ipfw -q add 00220 deny log ip from me to any in ipfw -q add 00225 deny log tcp from any to any in tcpflags syn,fin # check the traffic's state, let it in if we sent it, otherwise deny ipfw -q add 00230 check-state ipfw -q add 00235 deny tcp from any to any in established ipfw -q add 00240 allow ip from any to any out keep-state # allow traffic controlling icmp ipfw -q add 00300 allow icmp from any to any icmptype 3 ipfw -q add 00301 allow icmp from any to any icmptype 4 ipfw -q add 00302 allow icmp from any to any icmptype 11 # allow dhcp from dhcp servers ipfw -q add 00401 allow udp from 24.128.1.35 67 to any 68 ipfw -q add 00402 allow udp from 24.128.1.34 67 to any 68 # allow ident requests ipfw -q add 00500 allow tcp from any to any 113 keep-state setup # log anything that falls through ipfw -q add 09000 deny log ip from any to any
In this case my DHCP servers are 24.128.1.35 and 24.128.1.34 which are the New England mediaone dhcp servers. Be sure to add your own DHCP servers in, or if your using statically assigned ip addresses remove those lines completely.
Notice how I'm also blocking tcp packets that have the bits SYN and FIN set. This is because in the wild, for the most part, packets are not seen with this combination. It is however used by nmap and queso to fingerprint the os, since different OSes respond differently to this packet type. So it's best to log it and drop it.
Also notice the rule for dstport 113. Even if you're not running ident, it's best to put this rule in here so ident requests aren't logged. It's very annoying to have someone email you threats because their firewall caught an ident request from your network. Ident requests are not hack attempts. Therefore logging them is pointless, and harassing people about them is also pointless.
If your not going to run ident, it's best to change the rule to deny, and remove the keep-state flag. So dynamic rules are not created. Once again, do not bother to log traffic denied by this rule. It's normal network activity and not an attempted network penetration.
Now to activate these new firewall rules, perform the following:
# sh /etc/firewall.rules
Now check your connectivity and see if everything is working properly. You may also need to load the module ipfw.ko, if you do however, be sure to set:
# sysctl -w net.inet.ip.fw.verbose=1
This is needed if you want to see the dropped packets in your logs.
Now if you want all this to be set up when your machine boots, you'll need to edit your /etc/rc.conf and add the following.
firewall_enable="YES" firewall_logging="YES" firewall_script="/etc/firewall.rules"
Next you'll want to do something useful with the information we're logging. Open up /etc/syslog.conf again and add the following lines.
!ipfw *.* /var/log/ipfw.log
What this will do is send all the ipfw related data to it's own log file. In this case called ipfw.log. Next, of course, you'll have to tell syslogd what to do with that log file. Edit /etc/newsyslog.conf and add the following.
/var/log/ipfw.log 600 3 100 * Z
This will do is tell syslog to create a new file /var/log/ipfw.log and give it 600 permissions, and rotate it 3 times after it reaches 100k in size, as well as compressing it when it after it's been rotated for the first time.
Next time we'll talk about how to actually read that ipfw data.
comments, suggestions, flames? aeonflux99@hushmail.com