This section describes methods for securing a FreeBSD system against the attacks that were mentioned in the previous section.
Most
systems have a password assigned to the
root
account. Assume that this password
is always at risk of being compromised.
This does not mean that the password should be disabled as the
password is almost always necessary for console access to the
machine. However, it should not be possible to use this
password outside of the console or possibly even with
su(1). For example, setting the entries in
/etc/ttys
to insecure
prevents root
logins to the specified
terminals. In FreeBSD, root
logins using
ssh(1) are disabled by default as
PermitRootLogin
is set to
no
in
/etc/ssh/sshd_config
. Consider every
access method as services such as FTP often fall through the
cracks. Direct root
logins should only
be allowed via the system console.
Since a sysadmin needs access to
root
, additional password verification
should be configured. One method is to add appropriate user
accounts to wheel
in
/etc/group
. Members of
wheel
are allowed to su(1) to
root
. Only those users who actually need
to have root
access should be placed in
wheel
. When using Kerberos for
authentication, create a .k5login
in
the home directory of root
to allow
ksu(1) to be used without having to place anyone in
wheel
.
To lock an account completely, use pw(8):
#
pw lock staff
This will prevent the specified user from logging in using any mechanism, including ssh(1).
Another method of blocking access to accounts would be to
replace the encrypted password with a single
“*
” character. This character
would never match the encrypted password and thus blocks user
access. For example, the entry for the following
account:
could be changed to this using vipw(8):
This prevents foobar
from logging in
using conventional methods. This method for access
restriction is flawed on sites using
Kerberos or in situations where the
user has set up keys with ssh(1).
These security mechanisms assume that users are logging in from a more restrictive server to a less restrictive server. For example, if the server is running network services, the workstation should not be running any. In order for a workstation to be reasonably secure, run zero or as few services as possible and run a password-protected screensaver. Of course, given physical access to any system, an attacker can break any sort of security. Fortunately, many break-ins occur remotely, over a network, from people who do not have physical access to the system.
Using Kerberos provides the ability to disable or change the password for a user in one place, and have it immediately affect all the machines on which the user has an account. If an account is compromised, the ability to instantly change the associated password on all machines should not be underrated. Additional restrictions can be imposed with Kerberos: a Kerberos ticket can be configured to timeout and the Kerberos system can require that the user choose a new password after a configurable period of time.
The prudent sysadmin only enables required services and is
aware that third party servers are often the most bug-prone.
Never run a server that has not been checked out carefully.
Think twice before running any service as
root
as many daemons can be run as a
separate service account or can be started in a
sandbox. Do not activate insecure
services such as telnetd(8) or rlogind(8).
Another potential security hole is SUID-root and SGID
binaries. Most of these binaries, such as rlogin(1),
reside in /bin
,
/sbin
, /usr/bin
, or /usr/sbin
. While nothing is
100% safe, the system-default SUID and SGID binaries can be
considered reasonably safe. It is recommended to restrict
SUID binaries to a special group that only staff can access,
and to delete any unused SUID binaries. SGID binaries can be
almost as dangerous. If an intruder can break an SGID-kmem
binary, the intruder might be able to read
/dev/kmem
and thus read the encrypted
password file, potentially compromising user accounts.
Alternatively, an intruder who breaks group
kmem
can monitor keystrokes sent through
ptys, including ptys used by users who login through secure
methods. An intruder that breaks the
tty
group can write to almost any
user's tty. If a user is running a terminal program or
emulator with a keyboard-simulation feature, the intruder can
potentially generate a data stream that causes the user's
terminal to echo a command, which is then run as that
user.
User accounts are usually the most difficult to secure. Be vigilant in the monitoring of user accounts. Use of ssh(1) and Kerberos for user accounts requires extra administration and technical support, but provides a good solution compared to an encrypted password file.
The only sure fire way is to star out as many passwords as
possible and use ssh(1) or Kerberos for access to those
accounts. Even though the encrypted password file
(/etc/spwd.db
) can only be read by
root
, it may be possible for an intruder
to obtain read access to that file even if the attacker cannot
obtain root-write access.
Security scripts should be used to check for and report changes to the password file as described in the Checking file integrity section.
Most modern kernels have a packet sniffing device driver
built in. Under FreeBSD it is called
bpf
. This device is needed for DHCP,
but can be removed in the custom kernel configuration file of
systems that do not provide or use DHCP.
Even if bpf
is disabled,
/dev/mem
and
/dev/kmem
are still problematic. An
intruder can still write to raw disk devices. An enterprising
intruder can use kldload(8) to install his own
bpf
, or another sniffing device, on a
running kernel. To avoid these problems, run the kernel at a
higher security level, at least security level 1.
The security level of the kernel can be set in a variety
of ways. The simplest way of raising the security level of a
running kernel is to set
kern.securelevel
:
#
sysctl kern.securelevel=1
By default, the FreeBSD kernel boots with a security level of
-1. This is called “insecure mode” because
immutable file flags may be turned off and all devices may be
read from or written to. The security level will remain at -1
unless it is altered, either by the administrator or by
init(8), because of a setting in the startup scripts.
The security level may be raised during system startup by
setting
kern_securelevel_enable
to
YES
in /etc/rc.conf
,
and the value of kern_securelevel
to the
desired security level.
Once the security level is set to 1 or a higher value, the append-only and immutable files are honored, they cannot be turned off, and access to raw devices is denied. Higher levels restrict even more operations. For a full description of the effect of various security levels, refer to security(7) and init(8).
Bumping the security level to 1 or higher may cause a
few problems to Xorg, as access
to /dev/io
will be blocked, or to the
installation of FreeBSD built from source as
installworld
needs to temporarily
reset the append-only and immutable flags of some files.
In the case of Xorg, it may be
possible to work around this by starting xdm(1) early
in the boot process, when the security level is still low
enough. Workarounds may not be possible for all secure
levels or for all the potential restrictions they enforce.
A bit of forward planning is a good idea. Understanding the
restrictions imposed by each security level is important as
they severely diminish the ease of system use. It will also
make choosing a default setting much simpler and prevent any
surprises.
If the kernel's security level is raised to 1 or a higher
value, it may be useful to set the schg
flag on critical startup binaries, directories, script files,
and everything that gets run up to the point where the
security level is set. A less strict compromise is to run
the system at a higher security level but skip setting the
schg
flag. Another possibility is to
mount /
and /usr
read-only. It should be
noted that being too draconian about what is permitted may
prevent detection of an intrusion.
One can only protect the core system configuration and
control files so much before the convenience factor rears its
ugly head. For example, using chflags(1) to set the
schg
bit on most of the files in /
and /usr
is probably
counterproductive, because while it may protect the files, it
also closes an intrusion detection window. Security measures
are useless or, worse, present a false sense of security, if
potential intrusions cannot be detected. Half the job of
security is to slow down, not stop, an attacker, in order to
catch him in the act.
The best way to detect an intrusion is to look for modified, missing, or unexpected files. The best way to look for modified files is from another, often centralized, limited-access system. Writing security scripts on the extra-security limited-access system makes them mostly invisible to potential attackers. In order to take maximum advantage, the limited-access box needs significant access to the other machines, usually either through a read-only NFS export or by setting up ssh(1) key-pairs. Except for its network traffic, NFS is the least visible method, allowing the administrator to monitor the filesystems on each client box virtually undetected. If a limited-access server is connected to the client boxes through a switch, the NFS method is often the better choice. If a limited-access server is connected to the client boxes through several layers of routing, the NFS method may be too insecure and ssh(1) may be the better choice.
Once a limited-access box has been given at least read
access to the client systems it is supposed to monitor, create
the monitoring scripts. Given an NFS
mount, write scripts out of simple system utilities such as
find(1) and md5(1). It is best to physically
md5(1) the client system's files at least once a day, and
to test control files such as those found in /etc
and /usr/local/etc
even more often.
When mismatches are found, relative to the base md5
information the limited-access machine knows is valid, it
should alert the sysadmin. A good security script will also
check for inappropriate SUID binaries and for new or deleted
files on system partitions such as /
and /usr
.
When using ssh(1) rather than NFS, writing the security script is more difficult. For example, scp(1) is needed to send the scripts to the client box in order to run them. The ssh(1) client on the client box may already be compromised. Using ssh(1) may be necessary when running over insecure links, but it is harder to deal with.
A good security script will also check for changes to
hidden configuration files, such as
.rhosts
and
.ssh/authorized_keys
, as these files
might fall outside the purview of the
MD5
check.
For a large amount of user disk space, it may take too
long to run through every file on those partitions. In this
case, consider setting mount flags to disallow SUID binaries
by using nosuid
with mount(8). Scan
these partitions at least once a week, since the objective is
to detect a break-in attempt, whether or not the attempt
succeeds.
Process accounting (see accton(8)) is a relatively low-overhead feature of FreeBSD which might help as a post-break-in evaluation mechanism. It is especially useful in tracking down how an intruder broke into a system, assuming the file is still intact after the break-in has occurred.
Finally, security scripts should process the log files, and the logs themselves should be generated in as secure a manner as possible and sent to a remote syslog server. An intruder will try to cover his tracks, and log files are critical to the sysadmin trying to track down the time and method of the initial break-in. One way to keep a permanent record of the log files is to run the system console to a serial port and collect the information to a secure machine monitoring the consoles.
A little paranoia never hurts. As a rule, a sysadmin can add any number of security features which do not affect convenience and can add security features that do affect convenience with some added thought. More importantly, a security administrator should mix it up a bit. If recommendations, such as those mentioned in this section, are applied verbatim, those methodologies are given to the prospective attacker who also has access to this document.
A DoS attack is typically a packet attack. While there is not much one can do about spoofed packet attacks that saturate a network, one can generally limit the damage by ensuring that the attack cannot take down servers by:
Limiting server forks.
Limiting springboard attacks such as ICMP response attacks and ping broadcasts.
Overloading the kernel route cache.
A common DoS attack scenario is to
force a forking server to spawn so many child processes that
the host system eventually runs out of memory and file
descriptors, and then grinds to a halt. There are several
options to inetd(8) to limit this sort of attack. It
should be noted that while it is possible to prevent a machine
from going down, it is not generally possible to prevent a
service from being disrupted by the attack. Read
inetd(8) carefully and pay specific attention to
-c
, -C
, and
-R
. Spoofed IP attacks will circumvent
-C
to inetd(8), so typically a
combination of options must be used. Some standalone servers
have self-fork-limitation parameters.
Sendmail provides
-OMaxDaemonChildren
, which tends to work
better than trying to use
Sendmail's load limiting options
due to the load lag. Specify a
MaxDaemonChildren
when starting
Sendmail which is high enough to
handle the expected load, but not so high that the computer
cannot handle that number of
Sendmail instances. It is prudent
to run Sendmail in queued mode
using -ODeliveryMode=queued
and to run the
daemon (sendmail -bd
) separate from the
queue-runs (sendmail -q15m
). For
real-time delivery, run the queue at a much lower interval,
such as -q1m
, but be sure to specify a
reasonable MaxDaemonChildren
to prevent
cascade failures.
syslogd(8) can be attacked directly and it is
strongly recommended to use
-s
whenever possible, and
-a
otherwise.
Be careful with connect-back services such as reverse-identd, which can be attacked directly. The reverse-ident feature of TCP Wrappers is not recommended for this reason.
It is recommended to protect internal services from
external access by firewalling them at the border routers.
This is to prevent saturation attacks from outside the LAN,
not so much to protect internal services from network-based
root
compromise. Always configure an
exclusive firewall which denies everything by default except
for traffic which is explicitly allowed. The range of port
numbers used for dynamic binding in FreeBSD is controlled by
several net.inet.ip.portrange
sysctl(8) variables.
Another common DoS attack, called a
springboard attack, causes the server to generate responses
which overloads the server, the local network, or some other
machine. The most common attack of this nature is the
ICMP ping broadcast attack. The attacker
spoofs ping packets sent to the LAN's broadcast address with
the source IP address set to the machine to attack. If the
border routers are not configured to drop ping packets sent to
broadcast addresses, the LAN generates sufficient responses to
the spoofed source address to saturate the victim, especially
when the attack is against several dozen broadcast addresses
over several dozen different networks at once. A second
common springboard attack constructs packets that generate
ICMP error responses which can saturate a server's incoming
network and cause the server to saturate its outgoing network
with ICMP responses. This type of attack can crash the
server by running it out of memory, especially if the server
cannot drain the ICMP responses it generates fast enough. Use
the sysctl(8) variable
net.inet.icmp.icmplim
to limit these
attacks. The last major class of springboard attacks is
related to certain internal inetd(8) services such as the
UDP echo service. An attacker spoofs a UDP packet with a
source address of server A's echo port and a destination
address of server B's echo port, where server A and B on the
same LAN. The two servers bounce this one packet back and
forth between each other. The attacker can overload both
servers and the LAN by injecting a few packets in this manner.
Similar problems exist with the
chargen port. These inetd-internal
test services should remain disabled.
Spoofed packet attacks may be used to overload the kernel
route cache. Refer to the
net.inet.ip.rtexpire
,
rtminexpire
, and
rtmaxcache
sysctl(8) parameters. A
spoofed packet attack that uses a random source IP will cause
the kernel to generate a temporary cached route in the route
table, viewable with netstat -rna | fgrep
W3
. These routes typically timeout in 1600
seconds or so. If the kernel detects that the cached route
table has gotten too big, it will dynamically reduce the
rtexpire
but will never decrease it to less
than rtminexpire
. This creates two
problems:
The kernel does not react quickly enough when a lightly loaded server is suddenly attacked.
The rtminexpire
is not low enough
for the kernel to survive a sustained attack.
If the servers are connected to the Internet via a T3 or
better, it may be prudent to manually override both
rtexpire
and rtminexpire
via sysctl(8). Never set either parameter to zero
as this could crash the machine. Setting both parameters to 2
seconds should be sufficient to protect the route table from
attack.
There are a few issues with both Kerberos and ssh(1)
that need to be addressed if they are used. Kerberos is an
excellent authentication protocol, but there are bugs in the
kerberized versions of telnet(1) and rlogin(1) that
make them unsuitable for dealing with binary streams. By
default, Kerberos does not encrypt a session unless
-x
is used whereas ssh(1) encrypts
everything.
While ssh(1) works well, it forwards encryption keys
by default. This introduces a security risk to a user who
uses ssh(1) to access an insecure machine from a secure
workstation. The keys themselves are not exposed, but
ssh(1) installs a forwarding port for the duration of the
login. If an attacker has broken root
on
the insecure machine, he can utilize that port to gain access
to any other machine that those keys unlock.
It is recommended that ssh(1) is used in combination
with Kerberos whenever possible for staff logins and
ssh(1) can be compiled with Kerberos support. This
reduces reliance on potentially exposed SSH
keys while protecting passwords via Kerberos. Keys should
only be used for automated tasks from secure machines as this
is something that Kerberos is unsuited to. It is recommended
to either turn off key-forwarding in the
SSH configuration, or to make use
of from=IP/DOMAIN
in
authorized_keys
to make the key only
usable to entities logging in from specific machines.
All FreeBSD documents are available for download at http://ftp.FreeBSD.org/pub/FreeBSD/doc/
Questions that are not answered by the
documentation may be
sent to <freebsd-questions@FreeBSD.org>.
Send questions about this document to <freebsd-doc@FreeBSD.org>.