Настройка окружения SELinux для на примере LAMP-сервера

Это третья статья из цикла


b74f96165d9448a3b00a90b9ad312889.png
И сегодня она попала в поток «Администрирование». Сегодня мы не будем писать модули или настраивать RBAC, а пойдем по пути наименьшего сопротивления и просто захарденим обычный LAMP-сервер при помощи готовой политики, включив необходимые настройки.
Если кто забыл, за аббривиатурой LAMP скрывается Linux, Apache, Mysql, PHP, т.е. это большая часть всех VDS, которые покупают люди для хранения своих личных блогов. Надеюсь, что этот поможет всем им стать немного безопаснее:)

Предположения


Итак, предполагаем, что:
32c03705077a40f0a00815ca844e305a.png
  1. Дистрибутив — CentOS 7×64, пользователь — root
  2. SELinux включен, загружена политика targeted
  3. Режим SELinux — enforcing

Подготовка


Если LAMP у вас уже установлен и настроен — можете пропустить
Установим стандартный комплект софта для LAMP:
[root@lamp ~]# yum install -y httpd mariadb-server php-fpm php-mysql
Минимально настроим софт:
/etc/php-fpm.d/www.conf
[www]
listen = 127.0.0.1:9009
user = apache
group = apache
pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 35
pm.max_requests = 500
pm.status_path = /status
request_terminate_timeout = 10s
request_slowlog_timeout = 1s
slowlog = /var/log/php-fpm/www-slow.log
security.limit_extensions = .php
php_admin_value[error_log] = /var/log/php-fpm/www-error.log
php_admin_flag[log_errors] = on
php_admin_value[memory_limit] = 128M
php_value[session.save_handler] = files
php_value[session.save_path] = /var/lib/php/session

/etc/httpd/conf.d/userdir.conf

UserDir enabled
UserDir www


AllowOverride FileInfo AuthConfig Limit Indexes
Options MultiViews Indexes
Require method GET POST OPTIONS
DirectoryIndex index.html index.htm index.php


SetHandler «proxy: fcgi://127.0.0.1:9009»




Запустим все необходимые сервисы:
[root@lamp ~]# systemctl enable httpd mariadb php-fpm
[root@lamp ~]# systemctl start httpd mariadb php-fpm
Добавим какого-нибудь пользователя, например phpbb:
[root@lamp ~]# useradd -Z user_u -m -g apache phpbb
[root@lamp ~]# chmod 750 /home/phpbb

И создадим простой тестовый файл с phpinfo ():
[phpbb@lamp ~]$ mkdir www
[phpbb@lamp ~]$ echo »» > www/info.php
Перейдем по ссылке…
1cd5400a86a24caab7f09e2f549c11ae.png
… и получим именно то, что получают все:)

Разбираемся с ошибками


В отличие от других мануалов, где следующим шагом идет «отключите SELinux», мы сейчас узнаем, почему так получилось и что можно сделать.
Для начала — установим консольные утилиты для управления политиками SELinux:
[root@lamp ~]# yum install -y policycoreutils-python policycoreutils-newrole policycoreutils-restorecond setools-console
А потом — включим нужные нам модули (командой semodule):
[root@lamp ~]# semodule -e apache
[root@lamp ~]# semodule -e mysql
Давайте посмотрим, с какими именно проблемами столкнулся apache при открытии этой страницы?
audit2allow -lb -t httpd_t
#============= httpd_t ==============
#!!! This avc can be allowed using one of the these booleans:
# httpd_enable_homedirs, httpd_read_user_content, httpd_unified
allow httpd_t httpd_user_content_t: file getattr;

Все верно: папка www (, а так-же папки web и public_html ) внутри домашней директории пользователя автоматически получает тип httpd_user_content_t, что и указано в правилах:
sesearch -T -s user_t -c dir -d

type_transition user_t user_home_dir_t: dir httpd_user_content_t «public_html»;
type_transition user_t user_home_dir_t: dir httpd_user_content_t «www»;
type_transition user_t user_home_dir_t: dir httpd_user_content_t «web»;


Лечение указано в выводе audit2allow, установка переменных выполняется командой setsebool (или semanage boolean).
[root@lamp httpd]# setsebool -P httpd_read_user_content=1
Обновляем страницу и получаем:
38c4f3a915d14db79359792ce32be633.png
Смотрим логи:
/var/log/httpd/error_log
[Tue Feb 28 21:15:04.555595 2017] [proxy: error] [pid 21586] (13)Permission denied: AH00957: FCGI: attempt to connect to 127.0.0.1:9009 (*) failed
[Tue Feb 28 21:15:04.555892 2017] [proxy_fcgi: error] [pid 21586] [client 192.168.56.101:57974] AH01079: failed to make connection to backend: 127.0.0.1

Все ясно: httpd не может коннектиться куда попало, httpd может ходить только куда нужно. Это логично: если веб-сервер вдруг соединяется по ssh, то явно происходит что-то странное.
Давайте посмотрим, куда веб-серверу ходить можно?
sesearch -A -s httpd_t -c tcp_socket -p name_connect -d -C
DT allow httpd_t port_type: tcp_socket name_connect; [ httpd_can_network_connect ]
DT allow httpd_t mythtv_port_t: tcp_socket name_connect; [ httpd_can_connect_mythtv ]
DT allow httpd_t zabbix_port_t: tcp_socket name_connect; [ httpd_can_connect_zabbix ]
DT allow httpd_t smtp_port_t: tcp_socket name_connect; [ httpd_can_sendmail ]
DT allow httpd_t mssql_port_t: tcp_socket name_connect; [ httpd_can_network_connect_db ]
DT allow httpd_t postgresql_port_t: tcp_socket name_connect; [ httpd_can_network_connect_db ]
ET allow httpd_t ocsp_port_t: tcp_socket name_connect; [ kerberos_enabled ]
DT allow httpd_t oracle_port_t: tcp_socket name_connect; [ httpd_can_network_connect_db ]
DT allow httpd_t gopher_port_t: tcp_socket name_connect; [ httpd_can_network_relay ]
DT allow httpd_t osapi_compute_port_t: tcp_socket name_connect; [ httpd_use_openstack ]
DT allow httpd_t mongod_port_t: tcp_socket name_connect; [ httpd_can_network_connect_db ]
DT allow httpd_t memcache_port_t: tcp_socket name_connect; [ httpd_can_network_relay ]
DT allow httpd_t memcache_port_t: tcp_socket name_connect; [ httpd_can_network_memcache ]
DT allow httpd_t http_cache_port_t: tcp_socket name_connect; [ httpd_can_network_relay ]
DT allow httpd_t cobbler_port_t: tcp_socket name_connect; [ httpd_can_network_connect_cobbler ]
DT allow httpd_t ftp_port_t: tcp_socket name_connect; [ httpd_can_connect_ftp ]
DT allow httpd_t ftp_port_t: tcp_socket name_connect; [ httpd_can_network_relay ]
DT allow httpd_t gds_db_port_t: tcp_socket name_connect; [ httpd_can_network_connect_db ]
DT allow httpd_t pop_port_t: tcp_socket name_connect; [ httpd_can_sendmail ]
DT allow httpd_t http_port_t: tcp_socket name_connect; [ httpd_can_network_relay ]
ET allow httpd_t http_port_t: tcp_socket name_connect; [ httpd_graceful_shutdown ]
ET allow httpd_t kerberos_port_t: tcp_socket name_connect; [ kerberos_enabled ]
DT allow httpd_t ldap_port_t: tcp_socket name_connect; [ httpd_can_connect_ldap ]
DT allow httpd_t ephemeral_port_type: tcp_socket name_connect; [ httpd_use_openstack ]
DT allow httpd_t ephemeral_port_type: tcp_socket name_connect; [ httpd_can_connect_ftp ]
DT allow httpd_t ephemeral_port_type: tcp_socket name_connect; [ httpd_can_network_relay ]
DT allow httpd_t squid_port_t: tcp_socket name_connect; [ httpd_can_network_relay ]
DT allow httpd_t mysqld_port_t: tcp_socket name_connect; [ httpd_can_network_connect_db ]

В квадратных скобках указаны переменные, которые отвечают за работу этого правила.
Итого: нужно добавить порт 9009 в один из типов, к которым разрешен коннект, а затем установить переменную httpd_can_network_relay в 1.
Новый порт добавляется при помощи команды semanage port:
[root@lamp httpd]# semanage port -a -t http_cache_port_t -p tcp 9009
[root@lamp httpd]# setsebool -P httpd_can_network_relay=1
Обновляем страницу и видим:
02ab324ae4ee4780840c8b6a1333466a.png

Что-то посложнее


Давайте теперь усложним задачу и поставим phpbb на этот хост.
установка phpBB 3.2.0
[phpbb@lamp ~]$ curl www.phpbb.com/files/release/phpBB-3.2.0.zip -O
[phpbb@lamp ~]$ unzip phpBB-3.2.0.zip
[phpbb@lamp ~]$ mv phpBB3/* www/
[phpbb@lamp ~]$ restorecon -R www/

Попробуем создать для себя базу:
[phpbb@lamp www]$ mysql -uroot
ERROR 2002 (HY000): Can’t connect to local MySQL server through socket '/var/lib/mysql/mysql.sock' (13)
Поищем, как разрешить пользователю соединяться с базой?
sesearch -R -A -s user_t -t mysql -C -d
DT allow user_t mysqld_var_run_t: dir { getattr search open }; [ selinuxuser_mysql_connect_enabled ]
DT allow user_t mysqld_db_t: dir { getattr search open }; [ selinuxuser_mysql_connect_enabled ]
DT allow user_t mysqld_t: unix_stream_socket connectto; [ selinuxuser_mysql_connect_enabled ]
DT allow user_t mysqld_t: unix_stream_socket connectto; [ selinuxuser_mysql_connect_enabled ]
DT allow user_t mysqld_var_run_t: sock_file { write getattr append open }; [ selinuxuser_mysql_connect_enabled ]
DT allow user_t mysqld_var_run_t: sock_file { write getattr append open }; [ selinuxuser_mysql_connect_enabled ]

Отлично, включаем selinuxuser_mysql_connect_enabled и продолжаем:
[root@lamp httpd]# setsebool -P selinuxuser_mysql_connect_enabled=1

Создаем базу и пробуем зайти в инсталляшку phpbb:
a3273ad118d945c287cc7fab28b0d50f.png
Почему так? Потому что httpd не может изменять пользовательские данные. Давайте узнаем, какие же он изменять может?

sesearch -A -s httpd_t -p write -t user -C -R -d
ET allow httpd_t httpd_user_rw_content_t: file { ioctl read write create getattr setattr lock append unlink link rename open }; [ httpd_builtin_scripting ]
ET allow httpd_t httpd_user_ra_content_t: dir { ioctl write getattr lock add_name search open }; [ httpd_builtin_scripting ]
ET allow httpd_t httpd_user_ra_content_t: dir { ioctl write getattr lock add_name search open }; [ httpd_builtin_scripting ]
ET allow httpd_t httpd_user_rw_content_t: dir { ioctl read write getattr lock add_name remove_name search open }; [ httpd_builtin_scripting ]
ET allow httpd_t httpd_user_rw_content_t: dir { ioctl read write getattr lock add_name remove_name search open }; [ httpd_builtin_scripting ]
ET allow httpd_t httpd_user_rw_content_t: dir { ioctl read write create getattr setattr lock unlink link rename add_name remove_name reparent search rmdir open }; [ httpd_builtin_scripting ]
ET allow httpd_t httpd_user_rw_content_t: dir { ioctl read write getattr lock add_name remove_name search open }; [ httpd_builtin_scripting ]
DT allow httpd_t user_tty_device_t: chr_file { ioctl read write getattr lock append }; [ httpd_tty_comm ]
ET allow httpd_t httpd_user_rw_content_t: sock_file { read write getattr append open }; [ httpd_builtin_scripting ]
DT allow httpd_t user_devpts_t: chr_file { ioctl read write getattr lock append }; [ httpd_tty_comm ]
ET allow httpd_t httpd_user_rw_content_t: lnk_file { ioctl read write create getattr setattr lock append unlink link rename }; [ httpd_builtin_scripting ]

Устанавливаем php-xml, включаем httpd_builtin_scripting и назначаем контекст httpd_user_rw_content_t на указанные файлы и папки (командой chcon):
[root@lamp httpd]# setsebool -P httpd_builtin_scripting=1
[phpbb@lamp www]$ chmod 660 config.php
[phpbb@lamp www]$ chcon -t httpd_user_rw_content_t cache/ store/ files/ config.php images/avatars/upload/
Получаем:
a47e775d38e64e1191d02e8763332385.png
Устанавливаем phpBB дальше, удаляем install и получаем работающий форум:
37af528fb5ee46769f147730a5049e22.png
Меняем контекст конфига обратно:
[phpbb@lamp www]$ chcon -t httpd_user_content_t config.php
Наслаждаемся безопасным форумом :)

Вместо послесловия


Не написав ни одной строчки кода, используя только знания из man-файлов и стандартную политику SELinux по-умолчанию, можно за 30 минут настроить безопасное окружения для стандартных сервисов. И это не только про LAMP: стандартная политика содержит 403 готовых модуля. Этого хватит для решения большинства задач, которые когда-либо встанут перед администратором. Не выключайте SELinux, не заставляйте Дэна плакать.
Бонус #1
Для тех, кто все это таки прочел — готовый образ из статьи.
Ссылка: ufile.io/5eced (846 Mb) (ссылка истечет 29.03.2017)
Пароли
root: root
phpbb: phpbb
mysql root: root
mysql phpbb: i6p0AYF1B4Hg
phpbb: kreon: kreon1

Бонус #2
Небольшой cheatsheet.
Команды
  • semodule — управляет списком модулей
  • sestatus — текущий статус SELinux
  • setenforce 1/0 — включить/выключить enforcing
  • audit2allow — утилита для генерации правил (и для подсказок)
  • sesearch — утилита для поиска правил в политике
  • seinfo — показывает информацию о типах, ролях, атрибутах итд
  • semanage — позволяет вносить изменения в политики
  • chcon — позволяет менять контекст на ФС
  • restorecon — востанавливает контекст по-умолчанию
  • setsebool — устанавливает переменную в on/off. С -P — пишет на диск
  • getsebool — получает переменную. -a — посмотреть все

Изменения политики
  • semanage port -a/-d -t httpd_port_t -p tcp 8044 — добавить/удалить номер порта к контексту
  • semanage fcontext -a/-d -t httpd_cache_t »/srv/http/cache (/.*)?» — добавить/удалить контекст для этой маски
  • semanage permissive -a/-d httpd_t — включить/выключить режим permissive для httpd_t

Аргументы к командам
  • id -Z — показывает контекст текущего пользователя
  • ls -Z — показывает контекст файлов
  • ps -Z — показывает контекст процессов
  • netstat -Z — показывает контекст соединений
  • usermod/useradd -Z связать пользователя с SELinux-пользователем
  • ausearch -m AVC — показывает нарушения политик

Комментарии (0)

© Habrahabr.ru