====== General impression of RSBAC ======
* Pros
* Very powerful enhancement of LINUX kernel
* Nice design, which allows bunch off cool features like transaction, secure_delete, fd hiding...
* Cool community, still small but efficient and reactive
* Cons
* The lack of documentations for some modules
* You need strong knowledge of UNIX system to setup well RSBAC policies
* For a best enforcement, small changes must be done to distribution (like restoring dynamic fd (/proc, /dev, shm))
* Their isn't any reference policies for now
\\
====== Howtos ======
===== Howto use rsbac git repository =====
Here is a quick'N dirty way to follow rsbac code sources:
^ step ^ Command ^ Desc ^
| 1 | git clone git://rsbac.org/linux-2.6 linux-2.6-rsbac | Like svk mirror |
| 2 | git branch -a | List available branches |
| 3 | git checkout -t remotes/origin/rsbac-1.4-2.6.33 | -t means track this branch |
| 4 | git log | Check if it's what we wanted |
| 5 | make menuconfig, .... | ... |
| 6 | git pull | Get branche update, goto step 4 |
| 7 | git pull -a | Get all branches update, goto step 2 |
\\
Here is how I send patch:
^ step ^ Command ^ Desc ^
| 1 | checkout -b mybranch | ie checkout -b rc-learning-fixes |
| 2 | editing, messing up with the tree, ... | ... |
| 3 | git commit -a | clear commit log (fix/add/mod/del) |
| 4 | *tests* | goto step 2 if it does not work |
| 5 | git format-patch -1 -s --subject-prefix='PATCH][BUILD' | generate the patch |
| 6 | mail the generated new files to last upstream commit author | step 5 tell you the patch name |
\\
===== Howto restore read only filesystem attributes =====
RSBAC does handle generic lists in memory for RO fs such as squashfs, but during the umount
it has no way to store them on disk. We'll go trought the steps for making that possible.
=== System architectures examples ===
Host system with rsbac kernel
|
|->/home/vm/ ; Directory for vm mountpoints
| /dns/ ; DNS vm mountpoint
| /dns_data/ ; DNS '/var' mountpoint for persistant data
| /web/
| /web_data/
|->/home/fs/ ; Directory for vm root squashfs
| /root.squash ; RO vm root filesystem
The boot process is:
* First host boot
* Then mount ro vm root fs:
* link /home/fs/root.squash to /dev/loop7
* mount /dev/loop7 /home/vm/dns
* mount /dev/loop7 /home/vm/web
* prepare vm chroot:
* mount -o bind dns_data to /home/vm/dns/var
* mount proc, dev, ...
* run services in chroot
\\
And the shutdown process is:
* kill services
* umount vm chroot
* losetup -d /dev/loop7
* Host can halt
Revelant rsbac logs are:
^ Log ^ Desc ^
| read_list(): list fd_gen on device 07:07 not found | no rsbac.dat on /dev/loop7 |
| rsbac_auth_p_capset_member(): adding AUTH capability... | setting new AUTH cap during vm boot |
| ... | |
| rsbac_get_vfsmount(): unknown device | squashfs return unknown device on get_vfsmount |
| do_umount() [sys_umount()]: umount failed -> calling rsbac_mount for Device 07:07 | on last umount RSBAC did not save attributes |
\\
We do not want to run rsbac with auth_learning mode always activated. This is how I manage to store attributes and restore them on startup.
=== Attributes saving ===
Squashfs files attributes are currently store in ram and linked to the device 07:07 (/dev/loop7).
That is a good point for our purpose, because memory stored lists access are blazing fast.
Before shutdown and once in a while:
* Mount /dev/loop7 to a temporary mountpoint
* Then save rsbac backup tools output on that mountpoint, ie: 'chroot /mnt/tmp-loop auth_back_cap -r / 2>&1 | grep -v 'RSBAC_EINVALIDTARGET$' > policy.commands'
* Finaly unmount the temporary mountpoint
Then the shutdown process can happen. Let see how to restore the policy.
Note: Obviously, the policy.commands file should be protected by another security policy.
=== Attributes restoring ===
Rsbac will keep attributes for our device until every mountpoints are umount.
The vm start process is now:
* link /home/fs/root.squash to /dev/loop7
* mount /dev/loop7 to /mnt/tmp-loop
* restore_policy, a small C program which does:
* read the previously saved policy.commands files
* chroot to /mnt/tmp-loop
* setuid to secoff
* restore every attributes
* continue start process
* umount /mnt/tmp-loop
And voila !
===== Howto protect sshd =====
==== Setup UM ====
First we need to setup RSBAC User Management modules. In /etc/nsswitch.conf we may have '[passwd|shadow|group]: compat rsbac', and in /etc/pam.d/system-auth '[auth|account|password|session] sufficient pam_rsbac.so' before any pam_unix.so lines. Then :
secoff$ rsbac_useradd -O && rsbac_groupadd -O
secoff$ rsbac_passwd -n secoff
==== Grant sshd ====
Sshd must be allow to change his identity during key exchange
secoff$ for user in 0 22; do
for param in "" "-e" "-f"; do
auth_set_cap $param FD add /usr/sbin/sshd $user
done
done
Now we want sshd to be allow to change his identity only to an user who have been properly authenticated (by entering his password).
secoff$ attr_set_fd AUTH FD auth_may_setuid 3 /usr/sbin/sshd
Note: if you use CONFIG_RSBAC_AUTH_DAC_*, CONFIG_RSBAC_DAC_* you must add caps for allowed user, because they don't use the special auth_may_setuid value : 'Last Auth...'
secoff$ for user in 400 "1000 3000"; do
for param in "-e" "-f"; do
auth_set_cap $param FD add /usr/sbin/sshd $user
done
done
==== Run sshd ====
Add the following parameter to sshd init script:
-o "HostbasedAuthentication no" -o "PasswordAuthentication yes" -o "ChallengeResponseAuthentication no" -o "UsePAM yes" -o "UsePrivilegeSeparation yes" -o "PermitRootLogin no"
==== Test ====
ssh secoff@your_server
If you use the unix password, then you'll get : 'rsbac_adf_request(): request CHANGE_OWNER, ..., prog_name sshd, prog_file /usr/sbin/sshd, uid 0, remote ip ..., target_type PROCESS, ...., attr owner, value 400, result NOT_GRANTED by AUTH'.
Now you have to use the rsbac password, like that the kernel will know that secoff have been authenticate, then he'll allow sshd to change his identity.
==== Conclusion ====
This is a pretty good security measure, because even if sshd get's cracked, it won't be able to gain secoff credential without his passw
ord.
===== Howto use RES module =====
RES allows to enforce user limits (ulimit). Here are somes examples:
secoff$ for user in -4 root; do
attr_set_user RES $user res_max fsize 250000 # user won t create file more than 1G (block size = 4096)
attr_set_user RES $user res_max stack 100000 # user stack won t get bigger than 100 KB
attr_set_user RES $user res_max nofile 1024 # user won t open more than 1024 fds at a time
attr_set_user RES $user res_min core -1 # user will coredump by default
attr_set_user RES $user res_max nproc 200 # user won t start more than 200 process
attr_set_user RES $user res_max as 100000000 # user s process won t get bigger than 100MB
done
secoff$ attr_set_user RES root res_role 0
And that's it !
user$ ulimit -a
core file size (blocks, -c) unlimited
data seg size (kbytes, -d) unlimited
file size (blocks, -f) 976
max locked memory (kbytes, -l) unlimited
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
stack size (kbytes, -s) 97
cpu time (seconds, -t) unlimited
max user processes (-u) 200
virtual memory (kbytes, -v) 97656
file locks (-x) unlimited
user$ ulimit -u 1024
-sh: ulimit: max user processes: cannot modify limit: Operation not permitted
user$ for i in `seq 0 250`; do sleep 5& done
....
[195] 12759
-sh: fork: Resource temporarily unavailable
===== Howto protect Kernel code against tampering =====
==== Howto protect /boot device ====
In this example, we don't want anyone, except secoff, to be able to update kernel in /boot.
First, let's protect /boot directory with RC
# We create a new FD rc_type
rc_set_item TYPE `rc_get_item list_unused_fd_type_nr` type_fd_name BOOT_FD
# We allow secoff to access this type
rc_set_item ROLE 1 type_comp_fd $BOOT_FD A
# We set /boot rc_type_fd
attr_set_fd RC FD rc_type_fd $BOOT_FD /boot
# Repeat this last one after mounting the device: When a directory get s mounted,
#its attributes are replaced by the ones of the new device's root.
This is good, but root can still access boot data directly through /dev/sda1.
# We create a new DEV rc_type
rc_set_item TYPE `rc_get_item list_unused_dev_type_nr` type_dev_name BOOT_Device
# We allow secoff to access this type
rc_set_item ROLE 1 type_comp_dev $BOOT_Device A
# We set /dev/sda1 rc_type
attr_set_file_dir -d DEV b8:1 rc_type $BOOT_Device
Finally we wan't secoff to be able to mount the device:
# Let s fool mount
attr_set_fd GEN FD fake_root_uid 3 /bin/mount
# And allow secoff to changes files in /boot, the GEN way:
attr_set_fd GEN FD linux_dac_disable 1 /boot # do that when /boot is mounted !
# or the CAP way:
attr_set_user -a CAP secoff min_caps FS_MASK
===== Howto use RSBAC transactions =====
==== How transactions works ====
RSBAC transactions works pretty much the same as SQL databases transactions...
It ease policies development, for example you can do without loads of NOT_GRANTED:
* set rc_type BIN_FD /bin
* set roles type_comp_fd 0 -EXECUTE -MAP_EXEC
* set roles type_comp_fd $BIN_FD EXECUTE MAP_EXEC
* rsbac_list_ta commit
There are four commands to use :
* rsbac_list_ta -t 900 begin: this creates a new transaction valid for 15 minutes
* rsbac_list_ta -t 900 -N ta_number refresh: this resets the transaction timeout
* rsbac_list_ta -N ta_number commit: this makes all the changes effective
* rsbac_list_ta -N ta_number forget: this discards all the changes done in transaction
To monitor what's going on, read the '/proc/rsbac-info/gen_lists' file
finally, there are two way to change attributes in a transaction, you can either
* pass the transaction number to the tool:
attr_set_fd -N ta_number RC FD rc_type 2 /whatever
* set the environment variable RSBAC_TA
`rsbac_list_ta -b -t 900 begin`
rsbac_menu ...
export RSBAC_TA=ta_number
==== User creation examples ====
Here is a small script which show how to use transaction for user creation purposes.
#!/bin/sh
function abord { echo -e "\n\e[1;33m*\e[1;32m $1\e[0;37m"; rsbac_list_ta -N $RSBAC_TA forget; exit 1 }
export PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin
if [ -z "$1" ]; then echo "usage: $0 username"; exit 1; fi;
export RSBAC_TA="`rsbac_list_ta -t 10 begin`"
rsbac_useradd -x 60 -w 30 -g users -p '' $1 || myabord "Could not create user"
rsbac_usermod -l 0 $1 || myabord "Could not usermod"
attr_set_user RC $1 rc_def_role `rc_get_item list_roles | grep ' Remote_Users$' | awk '{print $1}'` || myabord "Could not set def role"
attr_set_user CAP $1 min_caps IPC_LOCK || myabord "attr_set_user"
attr_set_user CAP $1 cap_ld_env 1 || myabord "attr_set_user"
rsbac_list_ta -N $RSBAC_TA commit || myabord "rsbac_list_ta"
mkdir -m 700 /home/$1
chown $1 /home/$1
===== Howto use network template =====
==== How networks templates works ====
First some documentation. For each network access, the adf will try to match the object with a network template from lowest to highest ordering number until it matches one. So general purpose templates should have high numbers, and more detailed templates should have low numbers.
* When a socket is created, it emits a CREATE request over a network object with unknown local and remote address. The template matching uses the local address.
* If the socket is binded/listened, it emits a BIND/LISTEN request over a network object with the desired local address and an unknown remote address. The template matching uses the local address. Then when a client connects to the server, all matching are done with the remote address and remote port. Except for GET_STATUS_DATA request, matching is done with remote address but local port.
* If the socket is connected and used with another endpoint, it emits CONNECT/SEND/RECEIVE requests, and the template matching uses the remote address.
* Finally, when the socket is closed, it emits a NET_SHUTDOWN/CLOSE request, and the template matching uses the remote address
==== Web server example ====
Let's try to enforce network access of a web server
First, we create network template, with rsbac_nettemp_def_menu, and for each template we create a new rc_netobj_type with the name + _NETOBJ
^ Name ^ Nr ^ Family ^ Type ^ Address ^ Protocol ^ Ports ^
| DNS_SRV | 53 | INET | DGRAM | nameserver/32 | UDP | 53:53 |
| TCP | 300000 | INET | STREAM | 0.0.0.0/0 | TCP | |
| UDP | 300001 | INET | DGRAM | 0.0.0.0/0 | UDP | |
| NETLINK | 300002 | NETLINK | RAW | | ANY | |
| HTTPS | 200443 | INET | STREAM | 0.0.0.0/0 | TCP | 443:443 |
| LAN_TCP | 100000 | INET | STREAM | 192.168.0.0/16 | TCP | |
\\
The NETLINK socket is a special socket family for asking network configuration to the kernel.
So first, we must allow apache to access this template.
Then apache will create a TCP socket. At this point the netobj is defined as follow : 'INET STREAM proto TCP local 0.0.0.0:0 remote 0.0.0.0:0'. So the TCP template will match.
Then apache will bind this socket. The netobj is defined as follow: 'INET STREAM proto TCP local 0.0.0.0:443 remote 0.0.0.0:0, attr sock_type, value STREAM', aka HTTPS
Finally, when a browser connects to the server, the netobj is defined as follow : 'INET STREAM proto TCP local eth0:SRV_ADDR:443 remote CLIENT_ADDR:35967' aka LAN_TCP
So here is the list of necessary type_comp apache needs to be available for lan clients:
^ Netobj ^ Access ^
| NETLINK_NETOBJ | CREATE GET_STATUS_DATA BIND RECEIVE SEND |
| TCP_NETOBJ | CREATE GET_STATUS_DATA MODIFY_SYSTEM_DATA |
| HTTPS_NETOBJ | BIND LISTEN MODIFY_SYSTEM_DATA NET_SHUTDOWN |
| LAN_TCP | ACCEPT GET_STATUS_DATA READ RECEIVE WRITE SEND NET_SHUTDOWN |
| UDP_NETOBJ | CREATE GET_STATUS_DATA MODIFY_SYSTEM_DATA IOCTL NET_SHUTDOWN |
| DNS_SRV_NETOBJ | CONNECT SEND RECEIVE |
\\
And here you go !