Pull to refresh

Песочница для linux-приложений

Всё чаще попадаются на глаза мнения о том, что в linux нет вирусов только из-за того, что он, дескать, слабо распространён как desktop-система. Вот получит он широкое распространение, так побежит туда малварь всякая. Но, если хорошенько подумать, парадным входом для троянов всегда был человеческий фактор. Ну скачали не пойми что из интернета, ну запустили… В итоге имеем сворованные пароли от аккаунтов форумов/im-клиентов/жж и т.п. (хотя использование keyring тут спасает). Хоть запущенный от обычного пользователя троян в системе (/usr, /etc) и не задержится, но пароли своровать и нагадить пользователю (rm -rf ~) ему вполне по силам.

В итоге родилась идея написать примитивную «песочницу» для недоверенных приложений (лично я с такими не сталкивался, ибо по счастливой случайности мне почти всегда хватает родных репозиториев). Назовём её Simple Sandbox (ssandbox). Смысл её таков:


1) Создаётся новый пользователь.
2) Ему предоставляются только минимальные права в системе.
3) Настраивается его окружение (например, настройки гнома, вайн и пр.).
4) Пользователь добавляется в необходимые группы (например, audio, pulse, video).
5) С помощью xhost позволяем ему использовать Х-сервер.
6) Запускается подозрительное приложение.
7) Вместе с ним запускается демон (watchdog), который следит за этим пользователем. Если процессы юзера слишком сильно гадят в системе (создаётся слишком много процессов, используется много места в ФС), то страж убивает все процессы этого пользователя.
8) За юзером чистится диск.

Но для всех основных частей скрипта необходимы права рута, а давать их обычному пользователю не очень хочется. Поэтому я создал группу ssandbox-allowed и добавил в неё всех пользователей, кому необходимо пользоваться песочницей. А в конфиг /etc/sudoers добавил след. запись:

%ssandbox-allowed ALL=NOPASSWD:/usr/local/bin/ssandbox-run

В начало скрипта я добавил следующие строки:

if [ `id -u` -ne 0 ]; then
sudo $0 $@
exit
fi


В итоге при запуске от пользователя, входящего в группу ssandbox-allowed, скрипт обращается к sudo, тот запускает тот же скрипт, но уже от суперпользователя.

(Кто не понял зачем эти странные телодвижения: выставление suid-bit на скрипт ничего не даст, это защита от symlink атаки.)

Вот полный текст скриптов, предложения приветствуются.

ssandbox-run — основной скрипт

#!/bin/sh

if [ `id -u` -ne 0 ]; then
sudo $0 $@
exit
fi

if [ -e /usr/local/etc/ssandbox/ssandbox.conf ]; then
. /usr/local/etc/ssandbox/ssandbox.conf
else
echo "Need ssandbox.conf!"
exit 1
fi

if [ -z "$1" ]; then
echo "Need argument"
exit 2
fi

cleanup()
{
echo "Deleting profile..."
delete_user $SS_USER
echo "OK"
}

# Create new user
SS_USER=`ss_gen_name`
SS_HOME=/home/$SS_GROUP/$SS_USER
echo "user $SS_USER, home $SS_HOME"
adduser --gecos Untrusted \
--disabled-password \
--home $SS_HOME \
--ingroup $SS_GROUP \
$SS_USER
SS_UID=`cat /etc/passwd | grep $SS_USER | cut -d ':' -f 3`
trap 'cleanup' EXIT
chown -R $SS_USER:$SS_GROUP $SS_HOME

# Copy skel/ to user home
if [ "$COPY_SKEL" = "y" ]; then
[ -d $SKEL_DIR ] && cp -R $SKEL_DIR $SS_HOME
chown -R $SS_USER:$SS_GROUP $SS_HOME
fi

# Run scripts
if [ "$RUN_SCRIPTS" = "y" ]; then
export SS_UID
export SS_HOME
export SS_USER
run-parts $SCRIPT_DIR
fi

# Add user to defined groups
for GROUP in $GROUPS; do
usermod -a -G $GROUP $SS_USER
done

# Allow connection to DISPLAY
if [ "$NEED_X" = "y" ]; then
xhost SI:localuser:$SS_USER
fi

# Run watchdog silently
export SS_USER
export SS_HOME
ssandbox-watchdog $SS_UID >/dev/null 2>&1 &

echo "Running $@..."
su $SS_USER -- -c "exec `echo $@`"


ssandbox-watchdog — демон, следящий за ребёнком в песочнице
#!/bin/sh

. /usr/local/etc/ssandbox/ssandbox.conf || exit 1
[ -n "$1" ] || exit 2

LOG=/var/log/ssandbox.log

echo "Watch for user $1." >> $LOG
while true; do

# Check process number
RUNNING="`ps -u $1 -U $1 | wc -l`"
RUNNING=$(($RUNNING - 1))
echo "Processes running $RUNNING" >> $LOG
if [ "$RUNNING" -gt "$MAX_PROCESS_NUM" ]; then
echo "Too many processes are running!" \
"Allowed $MAX_PROCESS_NUM, but $RUNNING are running. Killing process..." >> $LOG
break
fi

# Check used disk space
FILES=`find /tmp -maxdepth 1 -uid $1`
SPACES=`du -c $FILES $SS_HOME | tail -n 1 | sed 's/[^0-9].*//'`
echo "Space $SPACES" >> $LOG
if [ "$SPACES" -gt "$MAX_STORAGE" ]; then
echo "Too much space used! Allowed $MAX_STORAGE, but $SPACES used. Killing process..." >> $LOG
break
fi

sleep $CHECK_EVERY

# Check wether user exited
if [ -z "`cat /etc/passwd | cut -d ' ' -f 1 | grep $SS_USER`" ]; then
echo "User was deleted." >> $LOG
exit
fi
done

killall -u $SS_USER su
killall -u $SS_USER
echo "User deleted!!!" >> $LOG


ssandbox.conf — конфигурационный файл
ss_gen_name()
{
local NAME="ssandbox"
local i=1
while [ -n "`cat /etc/passwd | cut -d ':' -f 1 | grep $NAME$i`" ]; do
i=$(($i+1))
done

echo $NAME$i
}

group_to_gid()
{
cat /etc/group | grep "^$1" | cut -d ':' -f 3
}

uid_to_name()
{
cat /etc/passwd | grep "^[^:]*:[^:]:$1" | cut -d ":" -f 1
}

delete_user()
{
SS_USER=$1
SS_HOME=/home/$SS_GROUP/$SS_USER
rm -rf $SS_HOME
xhost -SI:localuser:$SS_USER
deluser $SS_USER
}

# Is user able to open display? y/n
# Need for all x programs
NEED_X=y

# Group of all test users.
# This group is UNTRUSTED.
SS_GROUP="ssandbox"

# Run scripts in /etc/ssandbox/scripts.d/ ? y/n
RUN_SCRIPTS=n
SCRIPT_DIR=/usr/local/etc/ssandbox/scripts.d/

# Copy files from skel/ to ~ ? y/n
COPY_SKEL=n
SKEL_DIR=/usr/local/etc/ssandbox/skel/

# In whitch groups we need add new users.
# Good choise is audio pulse video
GROUPS=audio

######################
# Quotas for watchdog

# How many processes can be run at the same time?
MAX_PROCESS_NUM=30

# How much space in bytes on hard drive can processes use?
#MAX_STORAGE=1000000
MAX_STORAGE=100

# Check every ... seconds:
CHECK_EVERY=5


Установочный скрипт:
#!/bin/sh

#DIR=/
DIR=/usr/local
addgroup ssandbox
addgroup ssandbox-allowed
echo "%ssandbox-allowed ALL=NOPASSWD:$DIR/bin/ssandbox-run" >> /etc/sudoers
mkdir -p /home/ssandbox/

install -D ./ssandbox-run $DIR/bin/ssandbox-run
install -D ./ssandbox-watchdog $DIR/bin/ssandbox-watchdog
install -D ./ssandbox-cleanup $DIR/bin/ssandbox-cleanup
install -D ./ssandbox.conf $DIR/etc/ssandbox/ssandbox.conf
install -d $DIR/etc/ssandbox/skel
install -d $DIR/etc/ssandbox/scripts.d/


Если что-то пошло не так, то вот такой скрипт удаляет обрезки старых песочных пользователей:
#!/bin/sh

if [ -e /usr/local/etc/ssandbox/ssandbox.conf ]; then
. /usr/local/etc/ssandbox/ssandbox.conf
else
echo "Need ssandbox.conf!"
exit 1
fi

SS_GID=`group_to_gid $SS_GROUP`
UIDS=`cat /etc/passwd | grep "^[^:]*:[^:]*:[^:]*:$SS_GID" | cut -d ":" -f 3`
UID_NUM=`echo $UIDS | wc -w`
echo "Found $UID_NUM ssandbox sessions."

for DEL_UID in $UIDS; do
echo -n "Deleting user UID=$DEL_UID..."

PS_NUM=`ps -u $DEL_UID -U $DEL_UID | wc -l`
PS_NUM=$(($PS_NUM - 1))
echo " Found $PS_NUM running processes."

if [ "$PS_NUM" -eq 0 ]; then
echo -n "Delete user..."
#TODO:
SS_USER=`uid_to_name $DEL_UID`
echo $SS_USER
delete_user $SS_USER
echo "OK"
else
echo "Not all processes are finished, skip UID."
fi
done

echo "Finished."


Теперь попробуем протестировать это чудо инженерной мысли. Попробуем создать очень большой файл:

segooon@comp:~$ ssandbox-run bash
ssandbox1@comp:~$ yes > 1

И тут же (0-5 сек) юзера прибивает страж. Через пару секунд от песочного человека не осталось ни рожек, ни ножек.
Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.