Есть несколько способов обезопасить себя и свою машину во время веб-серфинга и выполнения других сетевых задач. Наиболее простой — завести в системе специального юзера, от имени которого заходить на безопасные/опасные сайты. Другой способ — это поднять виртуальную машину, установив на нее, например, Hardened Gentoo. Еще можно установить Qubes OS, но все это слишком избыточные конфигурации, которые можно заменить контейнерной виртуализацией.
Постановка задачи
За 2011 год во всех популярных браузерах суммарно было найдено 594 уязвимости, из которых браузеру Chrome принадлежали 278, а Firefox — 89. 197 уязвимостей в Chrome относились к классу высокой степени опасности плюс одна критическая уязвимость, в то время как в Firefox уязвимостей с высокой степенью опасности оказалось 65. В следующем году в Chrome обнаружили 125 уязвимостей, из которых 68 имели высокую степень опасности, в Firefox соотношение уязвимостей стало 159/99.
Chrome 29, выпущенный 20 августа 2013 года, закрыл 25 уязвимостей, часть из которых имели высокий уровень опасности. 13 ноября того же года появился Chrome 30 с 12 закрытыми уязвимостями, отдельные из которых приводили к раскрытию личных данных пользователей. Выпущенный 2 декабря Chrome 31 закрывал 25 проблем безопасности, одна из которых носила критический статус, а 21 — высокий. Chrome 32, выпущенный 14 января 2014 года, избавился от 21 уязвимости, 16 из которых с высоким статусом опасности.
Я мог бы продолжать этот скучный список еще долго, но картина уже должна быть ясна. Браузеры изрядно дырявы, и со временем особо ничего не меняется, а где есть дыры, там есть и те, кто их эксплуатирует. Современный браузер, который уже начал превращаться в полноценную операционную систему, — это огромная махина из миллионов строк кода, и многие из них могут быть потенциально опасны.
Если злоумышленнику удастся поломать защиту браузера и получить контроль над ним и пользовательскими данными, а что еще хуже — выйти за пределы браузера в операционную систему, скомпрометированными окажутся не только браузер, пароли и учетные записи для разных сайтов, но и вообще все пользовательские файлы и даже операционная система вместе с ядром. Все это приведет к таким забавным последствиям, как, например, угон аккаунтов, абсолютно прозрачный фишинг или перехват трафика, угон баз паролей, хранящихся в другом месте, скрытый майнинг биткоинов и черт его знает к чему еще.
В свое время Иоанна Рутковская, получившая неофициально прозвище Руткитовская, наглядно показала, что веб-браузинг может быть достаточно безопасным только в том случае, если разные инстанции браузера для разных типов сайтов будут выполняться в обособленных виртуальных окружениях, неспособных к взаимодействию друг с другом. Идея легла в основу операционной системы Qubes OS, которая представляет собой своеобразный Linux-дистрибутив, построенный на базе технологий Xen.
К большому сожалению, в силу своей архитектуры Qubes OS оказалась мало пригодной для обычных пользователей, которым от машины нужны не только возможность запускать браузер и офисный пакет, но и возможность устанавливать проприетарные драйверы, большой репозиторий софта и куча других возможностей. Поэтому в рамках данной статьи я хочу показать, как идею разделения браузера и задач можно реализовать в любом Linux-дистрибутиве без каких-либо серьезных ограничений.
Инструменты
В Qubes OS Рутковская использовала Xen, обосновав свой выбор логически более низким уровнем изоляции (ниже ядра Linux) и сравнительно небольшим объемом кода гипервизора, найти ошибку в котором по логике должно быть гораздо сложнее, чем в ядре Linux (если речь идет о KVM или, например, OpenVZ). Для нас же Xen не слишком удачное решение, которое создает слишком много ограничений. KVM также не очень удобен, а вот контейнерная виртуализация вроде OpenVZ или LXC подойдет очень даже хорошо.
Контейнерный тип виртуализации не так безопасен, как Xen и даже KVM, но его уровня изоляции будет более чем достаточно для решения нашей задачи. Я лично предпочитаю использовать LXC, но выбор инструмента и дистрибутива тут совсем не принципиален. Также я хотел бы обратить внимание на надстройку для LXC под названием Docker, которая существенно упрощает развертывание виртуальных окружений.
Мы воспользуемся Docker для того, чтобы посмотреть, как быстрее и проще всего можно развернуть виртуальное окружение с браузером внутри. Затем попробуем использовать LXC как более гибкое решение, позволяющее создать несколько логически разделенных окружений. Одно будет предназначено для «развлекательного» веб-серфинга, для сайтов вроде YouTube, ivi.ru, Last.fm и посещения разных блогов и новостных сайтов. Второе окружение будет рабочим (документы, таблицы, корпоративный сайт и так далее). Третье — интернет-банкинг и финансы: WebMoney, PayPal, Яндекс.Деньги.
Разделив задачи между разными виртуальными окружениями, мы решим сразу несколько проблем:
- Компрометация браузера во время серфинга по разным развлекательным сайтам не приведет к утечке рабочих и финансовых данных.
- В случае получения контроля над одним из браузеров/окружений остальные два, а также основная система останутся в безопасности (по крайней мере до тех пор, пока взломщик не найдет способ выйти за пределы окружения).
- Для разных типов сайтов мы сможем использовать разные браузеры и настройки (в «финансовом» окружении, например, нет смысла устанавливать расширения, а в некоторых случаях можно обойтись без JavaScript).
- Мы сможем делать снимки окружений для быстрого восстановления или переноса на другую машину.
Docker
Самый простой способ развернуть окружение на базе LXC — это воспользоваться Docker. В Ubuntu, Arch Linux и многих других дистрибутивах Docker есть в основном репозитории, поэтому для его установки достаточно набрать одну команду:
$ sudo apt-get install docker # Ubuntu
$ sudo pacman -S docker # Arch
Далее запускаем docker-демон. В Arch/Fedora так:
$ sudo systemctl start docker
В Debian/Ubuntu так:
$ sudo service docker start
Теперь мы готовы развернуть новое окружение. Сделать это не сложнее, чем установить сам Docker:
$ sudo docker run ubuntu top
Эта команда загрузит базовый образ окружения Ubuntu с сайта проекта, создаст новый LXC-контейнер, поднимет виртуальный сетевой интерфейс и запустит команду top внутри него. Размер образа — около 650 Мб, внутри минимальная система. Для загрузки также доступны Fedora, Debian, минималистичный образ с BusyBox и множество других. Доступны также и неофициальные образы, наиболее примечательный из которых magglass1/docker-browser-over-ssh. Это уже готовая сборка Ubuntu с предустановленным Google Chrome, Firefox и форвардингом X11 через SSH.
В этот раз мы не будем сразу запускать контейнер, а просто получим нужный образ:
$ sudo docker pull magglass1/docker-browser-over-ssh
Когда образ будет получен, запускаем контейнер:
$ sudo docker run -rm -t -i magglass1/docker-browser-over-ssh
После недолгой инициализации на экране появятся примерно такие строки:
IP address: 172.17.0.2
Password: zKksgadv8VbunE5D
Firefox: ssh -X webuser@172.17.0.2 firefox
Google Chrome: ssh -X webuser@172.17.0.2 google-chrome --no-sandbox
Это IP-адрес окружения (он может изменяться между запусками), рандомно сгенерированный пароль и команды для запуска браузеров. Теперь достаточно открыть второй эмулятор терминала и набрать одну из них. Браузер будет открыт в отдельном окне. Не самый удобный способ, зато он имеет ряд преимуществ:
- Благодаря особенностям Docker каждый новый запуск браузера будет происходить «в чистую». Другими словами, нам не потребуются разные варианты окружения для разных задач. Для того чтобы зайти на потенциально небезопасный сайт или интернет-банк, достаточно просто запустить новое окружение, выполнить работу и закрыть.
- Такой сэндбокс легко развернуть на любой доступной Linux-машине. Главное — наличие интернета.
- SSH-форвардинг позволяет полностью отрезать контейнер от основной системы.
- Достаточно высокая производительность браузера.
Из недостатков можно отметить отсутствие мультимедиавозможностей. Видео будет играть не совсем плавно, а звук не поддерживается.
LXC и Xephyr
Теперь попробуем создать несколько однотипных окружений для разных целей. В этот раз воспользуемся Ubuntu, чистым LXC и X-сервером Xephyr, последний позволит нам создать виртуальный X-сервер в гостевой машине, картинка которого будет отображаться внутри десктопа хостовой системы. Этот вариант не многим лучше проброса по SSH, он просто другой.
LXC у нас уже установлен как зависимость Docker, поэтому доустанавливаем debootstrap и bridge-utils:
$ sudo apt-get install debootstrap bridge-utils
Теперь создаем новый контейнер. Назовем его web-browser:
$ sudo lxc-create -t ubuntu -n web-browser
Как только образ будет выкачан по сети, запускаем контейнер:
$ sudo lxc-start -n web-browser
Устанавливаем нужный софт в контейнер:
$ apt-get update
$ apt-get install xterm openbox firefox
Теперь пишем небольшой скрипт, который поможет нам запустить Firefox внутри псевдодесктопа:
#!/bin/sh
# Запускаем вложенный X-сервер
Xephyr -ac :1 -screen 1024x768
# Запускаем Openbox и Firefox внутри этого сервера
lxc-start -n web-browser -- bash -c "export DISPLAY=IP-ХОСТ-СИСТЕМЫ:1; openbox & firefox"
Не спешим запускать контейнер, а вместо этого клонируем его для получения нескольких контейнеров с браузером внутри:
$ sudo lxc-clone -o web-browser -n web-banking
$ sudo lxc-clone -o web-browser -n web-other
Так мы получим несколько контейнеров, каждый из которых будет предназначен для разных целей. Скрипт запуска также можно скопировать плюс написать десктоп-файлы для создания соответствующих иконок:
[Desktop Entry]
Version=1.0
Name=Firefox
Comment=Access the Internet
# Путь до скрипта
Exec=/home/ЮЗЕР/bin/web-browser.sh
# Путь до иконки
Icon=/usr/share/pixmaps/firefox.png
Type=Application
Categories=Network;WebBrowser;
Чтобы иконка появилась на рабочем столе, ее следует положить в каталог ~/Desktop
.
LXC и контейнеры пространства пользователя
У описанных способов запуска браузера в контейнере есть один существенный недостаток: они работают с правами root и располагаются в корневой файловой системе. С точки зрения безопасности и здравого смысла это не совсем правильно, поэтому гораздо лучше запускать контейнеры в пространстве пользователя.
К сожалению, требуемая функциональность доступна только в разрабатываемой на момент написания статьи Ubuntu 14.4 и еще не вышедшем LXC 1.0. Зато инструкция, как это сделать, уже есть, и написана она Стефаном Грабером, тем самым человеком, который занимается поддержкой пакета LXC в Ubuntu. Ни в коем случае не претендуя на авторство метода, я приведу здесь суть его цикла статей, что будет интересно и пользователям Ubuntu 14.4, и всем тем, кто хочет узнать, на что способен LXC.
Итак, контейнеры пространства пользователя используют так называемые user namespaces, появившиеся в ядре Linux 3.0.12. Они позволяют отображать определенные диапазоны UID и GID из хост-системы в произвольные диапазоны UID:GID внутри контейнера, благодаря чему непривилегированного юзера хост-системы можно сделать root’ом внутри контейнера. Вся эта функциональность контролируется ядром плюс несколькими модифицированными утилитами вроде нового loginuid и утилит пакета shadow. Также появилась утилита lxc-user-nic с setuid-битом, которая позволяет управлять ограниченным набором сетевых настроек от имени непривилегированного пользователя. В основном она используется для изменения настроек сетевого моста.
Специально для создания userspace-контейнеров разработчики LXC подготовили шаблон download, который автоматически выкачивает из Сети регулярно обновляемую сборку Debian или Ubuntu и создает на ее основе новое виртуальное окружение. Чтобы получить эту сборку (в данном случае речь идет об Ubuntu 12.04) и создать новый контейнер (назовем его опять же web-browser), достаточно выполнить одну простую команду:
$ lxc-create -t download -n web-browser -- -d ubuntu -r precise
Далее необходимо сделать так, чтобы в контейнер были проброшены необходимые для вывода картинки файлы устройств, а также сокет X11. Для этого открываем конфиг контейнера~/.local/share/lxc/web-browser/config
и пишем в него следующее:
# `/dev/dri` и `/dev/video0` для доступа к видеокарте
lxc.mount.entry = /dev/dri dev/dri none bind,optional,create=dir
lxc.mount.entry = /dev/video0 dev/video0 none bind,optional,create=file
# `/dev/sound` для вывода звука
lxc.mount.entry = /dev/snd dev/snd none bind,optional,create=dir
# Сокет X11 для создания окна в запущенных в хост-системе иксах
lxc.mount.entry = /tmp/.X11-unix tmp/.X11-unix none bind,optional,create=dir
Далее находим в конфиге следующие строки:
lxc.id_map = u 0 100000 65536
lxc.id_map = g 0 100000 65536
И заменяем их на такие, поставив вместо 1000 собственные UID и GID в системе:
lxc.id_map = u 0 100000 1000
lxc.id_map = g 0 100000 1000
lxc.id_map = u 1000 1000 1
lxc.id_map = g 1000 1000 1
lxc.id_map = u 1001 101001 64535
lxc.id_map = g 1001 101001 64535
Все это означает, что UID/GID от 0 до 65535 в гостевой системе будут равны хостовым 100000– 165535. Исключением будет только UID:GID 1000 (то есть наш UID в хост-системе), он должен быть одинаковым в обеих системах, что позволит гостю работать с иксами, видео и аудиодрайвером. Проще говоря, хост-система будет воспринимать запущенный в контейнере браузер как «родное» приложение.
Далее исправляем владельца домашнего каталога внутри контейнера на наш UID:GID:
$ sudo chown -R 1000:1000 ~/.local/share/lxc/web-browser/rootfs/home/ubuntu
Устанавливаем Chrome внутрь контейнера:
$ C="web-browsing"
$ lxc-start -n $C -d
$ lxc-attach -n $C -- umount /tmp/.X11-unix
$ lxc-attach -n $C -- apt-get update
$ lxc-attach -n $C -- apt-get dist-upgrade -y
$ lxc-attach -n $C -- apt-get install wget ubuntu-artwork ca-certificates -y
$ lxc-attach -n $C -- wget https://dl.google.com/linux/direct/google-chrome-stable_current_i386.deb -O /tmp/chrome.deb
$ lxc-attach -n $C -- dpkg -i /tmp/chrome.deb
$ lxc-attach -n $C -- apt-get -f install -y
$ lxc-stop -n $C
Пишем скрипт для запуска хрома в контейнере:
#!/bin/sh
C="web-browser"
STARTED=false
if ! lxc-wait -n $C -s RUNNING -t 0; then
lxc-start -n $C -d
lxc-wait -n $C -s RUNNING
STARTED=true
fi
lxc-attach --clear-env -n $C -- sudo -u ubuntu -i env DISPLAY=$DISPLAY google-chrome --disable-setuid-sandbox
if [ "$STARTED" = "true" ]; then
lxc-stop -n $C -t 10
fi
Скрипт проверяет, запущен ли контейнер, если нет, то запускает и стартует внутри него Chrome. Основная суть всех этих действий состоит в том, чтобы не запускать уже работающий контейнер дважды, а также получить возможность создания и запуска нескольких разных контейнеров. Для этого достаточно клонировать уже готовый контейнер так, как описано в предыдущем разделе, и заменить имя web-browser на имя нового контейнера в начале скрипта.
Как и в случае со способом из предыдущего раздела, создаем десктоп-файл для запуска браузера по клику иконки:
[Desktop Entry]
Version=1.0
Name=Google Chrome
Comment=Access the Internet
Exec=/home/ИМЯ-ЮЗЕРА/.local/share/lxc/web-browser/start-chrome %U
Icon=/home/ИМЯ-ЮЗЕРА/.local/share/lxc/web-browser/rootfs/opt/google/chrome/product_logo_256.png
Type=Application
Categories=Network;WebBrowser;
На этом все. Такой контейнер будет лучшим решением из всех возможных. Он позволяет открывать несколько копий браузера в разных окружениях без всяких ограничений, с возможностью 3D-ускорения, выводом звука, плавным проигрыванием видео и без лишних виртуальных рабочих столов. Кстати, в оригинальном решении Стефана Грабера была также предусмотрена возможность записи звука с помощью PulseAudio, но я ее убрал для сохранения простоты.
Выводы
Контейнерная виртуализация прекрасно подходит для запуска потенциально небезопасного софта. Она работает в любом Linux-дистрибутиве, не требует сборки дополнительных инструментов и наложения патчей на ядро. Она более легковесна, чем полноценная виртуализация, более проста в развертывании и позволяет прозрачно работать с командной строкой и файлами внутри гостевой системы. Браузер всего лишь один из примеров приложений, которые можно запустить в контейнере.
Полезные ссылки
Оригинал статьи Стефана Грабера: goo.gl/qOa9F3