- PVSM.RU - https://www.pvsm.ru -
При управлении большим парком серверов (100 и более) в определенный момент возникает вопрос об упрощении выполнения рутинных задач.
Одно из главных требований в таких условиях — иметь полное представление о том, что и когда происходит на серверах, находящихся в зоне личной ответственности, но доступ к которым имеют еще как минимум несколько десятков разработчиков.
Сегодня мы поговорим об авторизации пользователей на Linux-серверах с использованием БД MySQL и приложения Puppet.
Итак, перед нами стояла задача внедрить централизованную систему управления доступом пользователей к серверам.
Безусловно, подобная система у нас уже была, и работала она вполне приемлемо, выполняла возложенные на нее задачи и в целом соответствовала нашим требованиям. Управление также было централизованным.
Но с течением времени стали появляться новые задачи, решать которые в существующих условиях становилось проблематично. И все чаще мы понимали: пора что-то менять в этой схеме. В конечном итоге было решено доработать текущую систему, чтобы она соответствовала нашим запросам.
В результате размышлений сотрудников IT-отдела появилось несколько идей:
У каждой из идей были свои плюсы и минусы. Например, в случае в LDAP (или LDAP плюс Kerberos) нам пришлось бы изучать дополнительные сервисы, решать вопросы оптимизации хранения данных в директории LDAP, устанавливать дополнительные LDAP-proxy для снижения нагрузки на основной сервер (потому как нагрузки у нас действительно большие). При выборе вариантов 2 и 4 мы получили бы тот же инструмент, который, увы, перестал удовлетворять нашим потребностям.
Однажды в понедельник (после основательного отдыха и в силу хорошего настроения) появилась одна интересная мысль: в нашей работе мы используем приложение Puppet, в том числе и для выдачи прав и полномочий пользователям. Так почему же не использовать его как основной инструмент?
Тут и началось самое интересное: готового решения не было, но существовал перечень обязательных требований к нашей системе:
И в качестве бонусов хотелось бы получить следующее:
В итоге было решено хранить структуры данных в MySQL (хранение в БД нам показалось удобным. Та структура, что есть в ней, может храниться и другими способами.), генерировать puppet-like манифесты «самописным» обработчиком, а также «приделывать» ко всему этому простой WEB UI. Забегая вперед, хочется сказать, что результат превзошёл наши ожидания, т.к. на этапе написания были добавлены важные и интересные моменты.
Сразу отметим, что мы имеем несколько географически удаленных площадок, где размещено оборудование; далее обозначим их «Платформа 1», «Платформа 2» и т.д. Обращаем ваше внимание, что приведенная ниже структура хранения данных показана только в качестве примера.
Сервера (узлы) в нашем примере будут иметь следующие обозначения:
1. Таблица Users — основная таблица, содержащая следующую информацию:
2. Таблица UsersWithBigUid — аналогична предыдущей, но используется для заведения «пользователей для служебных нужд» (например, существует географически удаленный пользователь, который не должен у нас нигде фигурировать, но ему необходим доступ к конкретному серверу, узлу или узлам).
3. Таблица VpnOnlyUsers:
4. Таблица TmplSudoersRules с описанием каждого правила sudo, входящего в тот или иной шаблон.
5. Таблица TmplSudoers, содержащая названия и комментарии к sudo-шаблонам.
6. Таблица SystemUsers — почти аналогична таблице с простыми пользователями, но используется для заведения пользователей с неограниченным правом доступа. В таблице присутствует дополнительное поле с идентификатором платформы на тот случай, если пользователь нужен только на одной из низ.
7. Таблица Sudoers содержит персонализированные правила sudo для пользователей.
8. Таблица Hostaliases состоит из алиасов имен серверов, которые в определенных случаях более удобны, чем имена, например, когда на один сервер возложено несколько ролей. С помощью данного функционала мы получаем еще один уровень абстракции и нам не важно имя узла.
1. Заполняем общую информацию о пользователе.
2. Выдаем доступ к серверам.
Примечание. Поля *servers — перечисление имен серверов и (или) групп через запятую. Пример заполнения данных полей:
3. Добавляем один или несколько sshpub-ключей.
4. Добавляем sudo-права пользователю.
После добавления пользователя и указания sudo-прав доступы обновятся на всех серверах в течение нескольких минут. Заведение пользователя на серверах можно ускорить, если в этом есть необходимость.
Давайте посмотрим, что нам нужно сделать для получения ACL для VPN. Все более чем просто:
1. Выбираем пользователя.
2. Открываем вкладку VPN, выбираем «Generate VPN». Получаем правило, которое нужно лишь добавить в RADIUS.
Примечание. Безусловно, данные сами по себе не появляются. В нашем случае обрабатываются поля со списком серверов, к которым пользователь имеет доступ. На основании этих данных и формируется VPN access-list. Здесь тоже предусмотрена определенная гибкость, т.е. мы можем выдать доступ как к 1 узлу в подсети, так и ко всей подсети. Дублирование отсутствует, т.е. если есть доступ в подсеть 10.11.12.0/24, то не имеет смысла добавлять что-либо наподобие “ip:inacl#N=permit ip any host 10.11.12.18”. В нашем примере полученные данные необходимо внести в конфигурацию авторизации VPN вручную (мы предлагаем вам самостоятельно поразмыслить, как это можно сделать — пусть это будет своего рода “домашним заданием”).
Задача 1: отозвать весь доступ у пользователя.
Решение: очистить поля с указанием серверов у пользователя, после чего он автоматически удалится на всех серверах (домашняя директория не удаляется).
Задача 2: отозвать доступ к определенному серверу.
Решение: убрать этот хост в общем списке хостов пользователя.
Задача 3: предоставить доступ к определенному серверу (серверам).
Решение: дописать нужный хост(-ы) к разрешенным у пользователя.
Задача 4: дать возможность перезапуска nginx на машинах в www-кластере группе лиц.
Решение:
a. Заводим новый sudoers-template, даем ему адекватное имя и комментарий.
INSERT INTO `tmplsudoers` (`tmplname`, `comments`)
VALUES
('suwwwrun', 'su to wwwrun user and restart nginx if needed');
Примечание. Данный пример указан в качестве альтернативы нашему веб-интерфейсу.
b. Добавляем правила, характерные для данного шаблона.
INSERT INTO `tmplsudoersrules` (`tmplid`, `hostname`, `runas`, `commands`, `nopasswd`)
VALUES
(26, 'www[0-9]*', 'ALL', '/bin/su - wwwrun', 1);
INSERT INTO `tmplsudoersrules` (`tmplid`, `hostname`, `runas`, `commands`, `nopasswd`)
VALUES
(26, 'www[0-9]*', 'ALL', '/etc/init.d/nginx *', 1);
c. Теперь нам достаточно добавить данный шаблон пользователю в интерфейс, после чего у него заработает весь набор правил.
Примечание. Помимо шаблонных правил мы можем задавать уникальные правила отдельным пользователям.
Задача 5: обеспечить безопасное добавление sudo, поскольку в некоторых случаях безобидное, на первый взгляд, sudo может нанести ущерб безопасности (например, на less, mount или rsync). Разрешить службе HelpDesk давать такие права сотрудникам.
Решение: шаблоны составляют опытные системные администраторы, а сотрудники HelpDesk только ставят соответствующие отметки.
Разрешить сотрудникам HelpDesk назначать шаблоны, но запретить их корректировку, а также запретить выдачу уникальных правил определенному пользователю.
Задача 6: сформировать sudoers таким образом, чтобы не нарушить работу сервера(-ов), потому что наличие некорректного синтаксиса и (или) дублирование директив HOSTALIASES может иметь плохие последствия.
Решение:
a. Перед добавлением или изменением sudoers-файла для пользователя производится проверка синтаксиса (в случае наличия ошибки формируется уведомление, файл sudoers для пользователя не изменяется).
Пример функции на Python:
VISUDO ='/usr/sbin/visudo'
def visudoCheck(filename,user):
visudo_cmd = 'echo -ne "%s "; echo "%s" | %s -c -f -' % (user,filename,VISUDO)
(visudo_status, visudo_output) = commands.getstatusoutput(visudo_cmd)
if not visudo_status == 0:
print "n"+filename
sys.stderr.write(visudo_output[:1024])
return visudo_status
b. Шаблоны составляют опытные системные администраторы, а сотрудники HelpDesk только ставят соответствующие отметки.
c. При формировании строк “Host_Alias” используются следующие правила во избежание дублирования:
Другие задачи решаются так же просто, ваши вопросы мы можем обсудить в комментариях.
Скрипт запускается лишь с одним параметром – идентификатором платформы, для которой формируются правила. После выполнения получаем подобный результат:
class virtualusers {
@group { "wwwaccess": gid => 1001, ensure => present }
@user { "petek":
ensure => $hostname ? {
simplehostname1 => present,
/hostgroup1(d.*)$/ => present,
/ hostgroup2(d.*)$/ => present,
/ hostgroup3(d.*)$/ => present,
…
…
default => absent,
},
comment => Petek Petkovich',
gid => '1001',
home => '/home/petek',
uid => '1018',
password => '*',
shell => '/bin/bash',
}
@virtualuser_key { "petek":
group => '1001',
name => 'petek',
key =>"ssh-rsa dqwedqwedqwedqedqwedqwedqwedqwedqewdqwed=",
require => User["petek"];
}
@exec { "petek_quota":
command => "/usr/sbin/setquota -u greli 5242880 5242880 0 0 -a /filesystem/",
path => "/usr/sbin",
onlyif => "/usr/bin/test `/usr/sbin/repquota -ua | /usr/bin/egrep '^peteks*' | /usr/bin/awk {'print $4'}` -ne "5242880"",
}
@file { "/etc/sudoers.d/1018_petek" :
ensure => present,
owner => 'root',
mode => 0440,
content => '';
}
if $hostname =~ /^hostgroup1d*$/ {
realize (Virtualuser_key['petek',’petek2’], File['/etc/sudoers.d/1018_petek', '/etc/sudoers.d/1020_petek2',])
realize (Exec['petek_quota'])
}
}
Для примера и читабельности оставлен только кусок манифеста. По правде говоря, читабельность манифестов нас не сильно интересует, в силу того что сразу после их формирования происходит проверка синтаксиса. Если он в полном порядке, то puppet master способен его прочитать, в таком случае нам незачем вмешиваться в процесс.
Приведем пример проверки синтаксиса:
PUPPET='/usr/bin/puppet'
def puppetparsercheck(filename):
puppet_cmd = '%s parser validate %s' % (PUPPET, filename)
(puppetparser_status, puppetparser_output) = commands.getstatusoutput(puppet_cmd)
print str(puppet_cmd)
if not puppetparser_status == 0:
sys.stderr.write(puppetparser_output[:1024])
return puppetparser_status
Таким образом, мы получили средство управления доступом пользователей к серверам, соответствующее всем нашим требованиям. Оно использует инструментарий, существовавший до момента его написания (что само по себе замечательно), а также избавило нас от внедрения и поддержки дополнительных сервисов.
И самое главное, пожалуй, то, что с такой системой способен справиться любой сотрудник «первой линии поддержки» (те, кто помогает с простыми просьбами пользователей).
Автор: Badoo
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/linux/6122
Нажмите здесь для печати.