Hello,
I would like to raise a discussion about the security features
of FreeBSD as a whole and how they might be employed to actually
derive some meaningful guarantees.
I have found myself administering a system with many potentially
untrusted users. Furthermore, some users do not trust some of the
programs they run and are thus allowed to ask for some "slave"
accounts. A "slave" account is a user account accessible only to
root and the "master" user. This can lead to a hierachy of authority.
Also, each account has potentially confidential data that may be
accessed only by the account itself and its "ancestor" accounts. This
includes when a user is logged on and what the user is running.
Finally, the system must always be up so no user untrusted by root
may trash it.
This is a pretty harsh set of restrictions and is almost unmanageable.
However, I have taken three steps to ensure security: base system
hardening, using sudo for privilege granting and using rctl(8) for
resource accounting and control. Gathering enough information in these
three areas has been an ongoing task for almost half a year, and I
would like to discuss some problems of my approach.
In terms of system hardening, I have:
* Encrypted the whole (except /boot) system with geli(8)
(HMAC/SHA256 and AES-XTS). It is not as nice and much slower
than proper filesystem-level checksumming but it is what
FreeBSD provides (ZFS is too unstable).
* Disabled useless and potentially dangerous services: cron, devd
and sendmail.
* Removed every setuid bit. The system works even then.
* Hardened /dev: every non necessary device has had the 0007 bits
stripped. Optional groups were created (e.g. audio, mixer and mic
for devices /dev/{mixer,dsp,audio}*).
* Hardened the sysctls:
- security.bsd.see_other_uids=0: Users can only see own processes.
- security.bsd.unprivileged_proc_debug=0
- security.bsd.unprivileged_read_msgbuf=0: The log is considered
sensitive information.
- security.bsd.hardlink_check_uid=1: Avoid hardlinks to old SUID
binaries.
- kern.log_console_output=0
- kern.coredump=0
- vm.overcommit=1: This avoids retarded Linux-like behaviour on
OOM conditions.
* Changed permissions on /root to 0700: root deserves privacy.
* A boot script changes some permissions:
- /var/log to 0750: the logs are considered sensitive information.
- /var/run/dmesg.boot to 0640: this is also sensitive information.
* Added a group sudoers and made sudo setuid only to users in
sudoers: would have avoided trouble with recent sudo exploit if
only trusted users have slaves.
As for using sudo to grant privilege, for each master-slave
relationship between users u and v, I have added a line like
"u ALL = (v) NOPASSWD: ALL" to /etc/sudoers. Then the user u is
supposed to become v by issuing "sudo -i -u v" and to execute a
command as v by issuing "sudo -i -u v ...".
It is worth noticing that sudo closes all file descriptors greater than
or equal to 3. It is important not to let your pseudo-terminal leak
through file descriptors 0, 1 and 2 if you have a shell connected to
it. Also, the "-i" is mandatory because otherwise a file descriptor
open at directory "." is leaked via the cwd file descriptor. I
believe this is enough, but since this is not properly documented, I
am not sure.
As for resource limiting via rctl(8), for each user u root does not
trust, I have added three rules:
* user:u:vmemoryuse:deny=<MEM>
* user:u:maxproc:deny=<PROCS>
* user:u:pseudoterminals:deny=0
Here <MEM> and <PROCS> are limits on total virtual memory usage and
total occupied entries in the process table for process u,
respectively. Furthermore, I never give access to pseudo-terminals to
untrusted users because all sessions are started from ssh or ptys of
trusted users. Also, ptys must be available otherwise trusted users can
not work on the machine. Finally, I have noticed "rctl -u user:u"
reports a single pty open for user u no matter how many open ptys u has
(except of course if u has no open pty, in which case 0 is reported).
One naively would expect these restrictions to be enough to prevent
abuse (trashing or DoS) as long as the sum of the MEM values (rounded
up to page size) is less than or equal to the total physical memory
plus swap space less the system (and trusted users') memory usage and
the sum of the PROC values is less than the process table size minus
the number of trusted processes. I sincerely do not know if this is the
case.
However, using vmemoryuse as a limit is overkill: it counts the total
mapped pages, not the total anonymous pages, which are the ones that
actually take resources. Of course, this assumes the memory management
data structures (including the page table) are accounted as anonymous
memory of the corresponding process, since it is easy (especially on
amd64), to map pages sparsely to greatly increase the size of the
page table. However, I do not know if this assumption holds on
FreeBSD.
What I would like here is a vmemoryuse-like switch that would count
anonymous pages only. Programs such as mmap(2)ping torrent clients tend
to have a virtual memory usage that far surpasses the anonymous memory
usage. Having read getrlimit(2) and having set a rctl(8) rule of the
form "user:u:swapuse:deny=<MEM>" (with vm.overcommit=3) hoping
it would
have a similar effect to RLIMIT_SWAP, I got several kernel panics if
<MEM> was low enough (32M when it happened). Also, the reports from
"rctl -u user:u" were inconsistent.
I would like to discuss with anyone interested in this topic,
especially about further precautions and the effectiveness of the
above ones.
FreeBSD, as I can see, is the best operating system out there in terms
of security (and I have tried several), even compared to Linux and
OpenBSD. However, it is really insecure out of the box like most
others. To secure it, one must perform contrived configurations and
tweaks, so I wonder why those are not default.
P.S.: If you want to attain desktop security, matters get even more
complicated. If anyone is interested, I can discuss what I did there
(basically virtual X servers and building ports as regular users).
Also, thanks for Capsicum, it sure is useful.