Вопрос или проблема
Используя дистрибутив RedHat (CentOS), я хотел бы сохранить список обычных пользователей (UID более 500) и файлов группы (и shadow) на резервном сервере.
Синхронизация односторонняя, с основного сервера на резервный.
Я действительно не хочу иметь дело с LDAP или NIS.
Мне нужен простой сценарий, который можно запускать ночью для обновления резервного сервера.
Основной сервер может подключаться по SSH к резервной системе.
Есть предложения?
Правка:
Спасибо за предложения, но думаю, я недостаточно ясно выразился.
Меня интересует синхронизация только обычных пользователей, чей UID равен или больше 500.
Системные/сервисные пользователи (с UID менее 500) могут отличаться на обеих системах.
Так что боюсь, нельзя просто синхронизировать все файлы.
Вы можете использовать awk для извлечения пользователей/групп с ID 500 или более. Я также исключил ID пользователя 65534, который часто зарезервирован для пользователя “nobody” (в зависимости от дистрибутива; не уверен, делает ли это CentOS):
awk -F: '($3>=500) && ($3!=65534)' /etc/passwd > passwd.new
awk -F: '($3>=500) && ($3!=65534)' /etc/group > group.new
awk -F: '($3>=500) && ($3!=65534) {print $1}' /etc/passwd | grep -f - /etc/shadow > shadow.new
Затем используйте rsync, scp или ваш метод передачи файлов, чтобы скопировать файлы на ваш резервный сервер. Эти файлы можно затем присоединить к концу ‘чистого’ файла passwd, group или shadow, когда потребуется их восстановить (т.е.: только системные пользователи/группы по умолчанию, чтобы предотвратить случайное дублирование ID/имя пользователя).
cat passwd.new >> /etc/passwd
cat group.new >> /etc/group
cat shadow.new >> /etc/shadow
NIS/NIS+ были изобретены именно для этой цели.
Но они довольно громоздкие и централизованная аутентификация (LDAP/Kerberos/SMB и т.д.) намного лучше, если вы можете её применить. Чтобы настроить NIS/NIS+ вам понадобятся:
Пакеты:
yp-tools ypbind ypserv portmap
и файл /etc/yp.conf с чем-то вроде:
domain example.org server nis.example.org
ypserver nis.example.org
и затем в /etc/sysconfig/network:
NISDOMAIN=example.org
И я стал ленивым, вот хорошая инструкция: http://www.wains.be/index.php/2007/02/28/setting-up-nis-under-centos-4/, которая пройдёт вас через процесс настройки.
Лично для резервного копирования я бы просто всю директорию /etc/ и закончил на этом. Это всего лишь несколько мегабайт в худшем случае.
Используйте cppw и cpgr:
CPPW(8)
NAME
cppw, cpgr - скопируйте с блокировкой заданный файл в
файл паролей или группы
СИНТАКСИС
cppw [-h] [-s] password_file
cpgr [-h] [-s] group_file
ОПИСАНИЕ
cppw и cpgr скопируют с блокировкой заданный файл в
/etc/passwd и /etc/group, соответственно. При использовании флага -s,
они скопируют теневые версии этих файлов,
/etc/shadow и /etc/gshadow, соответственно.
С флагом -h команды отображают короткое сообщение помощи
и скрыто выходят.
СМОТРИТЕ ТАКЖЕ
vipw(8), vigr(8), group(5), passwd(5), shadow(5), gshadow(5)
АВТОР
cppw и cpgr были написаны Стивеном Фростом, основаны на vipw
и vigr написанных Гаем Маором.
Существует много способов и решений, но чтобы ответить на первоначальный вопрос, нужно выполнить три шага:
-
Создайте SSH-ключ без пароля на сервере:
ssh-keygen -b 4096
-
Скопируйте .ssh/id_rsa.pub в .ssh/authorized__keys2 на клиенте:
scp ~/.ssh/id_rsa.pub client:.ssh/authorized_keys2
-
Добавьте что-то вроде этого в ваш /etc/crontab (или отредактируйте с помощью crontab -e):
0 0 * * * scp /etc/{passwd,shadow,group} root@backupbox:/var/mybackupdir
Я думал, что существовало что-то готовое, что я мог бы использовать без необходимости создавать своё решение, но мне пришлось быстро что-нибудь сделать.
Ниже приведён скрипт, который сделает всё, что мне нужно.
Инструкции
Чтобы он работал, достаточно изменить несколько конфигурационных переменных для минимального и максимального UID, чтобы они считались нормальными пользователями, и имя или IP-адрес удалённого хоста.
Вы должны настроить удалённый сервер на приём входящих сессий SSH от локального сервера под пользователем root
без необходимости ввода пароля.
Commander Keen подсказал, как это сделать в своём ответе на этой странице, но вы также можете обратиться к авторизации без пароля в SSH для получения подробных инструкций.
Как это работает
Что делает скрипт, так это копирует каждый из удалённых файлов passwd, group, shadow, gshadow с удалённого сервера во временное местоположение на локальном сервере.
Затем он удаляет из этих временных файлов всех “нормальных” пользователей, оставляя только ссылки на системных пользователей.
Следующий шаг — пройтись по каждой из локальных версий passwd, group, shadow, gshadow и добавляется только “нормальных” пользователей в их соответствующие временные файлы, затем загрузить каждый из них обратно на удалённый сервер, чтобы заменить старую версию.
Предупреждение
Прежде чем что-либо предпринимать, убедитесь, что у вас есть копии ваших passwd, group, shadow, gshadow на локальном и удалённом серверах.
Безопасность
Владельцы файлов и атрибуты сохраняются.
Временные файлы сохраняются в /tmp
и удаляются, независимо от того, была ли синхронизация успешной или нет.
Локальный сервер должен иметь доступ на основе пароля к резервной копии (но не наоборот). Это необходимо, чтобы мы могли получить файлы конфигурации учётных записей пользователей (которые в противном случае ограничены).
Код
Это первая попытка, и он немного грязный (не красивый код), но он довольно хорошо выполняет свою работу, и кто-то другой может найти его полезным.
Это Perl-скрипт, который имеет зависимость только от модуля Net::SCP
для безопасного копирования файлов между серверами.
#!/usr/bin/perl -w
use Net::SCP qw(scp);
use strict;
use constant TRUE => (1==1);
use constant FALSE => (1==0);
#--------------------------------------------------------
# Конфигурация
# Изменяйте по мере необходимости
#--------------------------------------------------------
my $remoteHost="10.13.113.2"; # сервер резервного копирования почты
my $minUID = 500;
my $maxUID = 30000;
my $minGID = 500;
my $maxGID = 30000;
#--------------------------------------------------------
# Внутренние переменные, обычно не требующие модификации.
#--------------------------------------------------------
my $systemConfigDir="/etc";
my $tmpDir = $ENV{TMPDIR} || $ENV{TMP} || $ENV{TEMP} || '/tmp';
#--------------------------------------------------------
# Основная часть
#--------------------------------------------------------
# ШАГ 1
# Получите удалённые файлы в /tmp и
# очистите их от обычных пользователей
ProcessFiles('remote');
# ШАГ 2
# Добавьте локальных обычных пользователей к временным файлам
# и затем отправьте их обратно на удалённый сервер
ProcessFiles('local');
#--------------------------------------------------------
# Подфункция ProcessFiles выполняет одну из двух вещей:
# - если передан аргумент 'remote', тогда получает каждый
# файл учётной записи пользователя с удалённого сервера, а затем удаляет
# всех обычных пользователей из каждого файла, оставляя только
# системных пользователей.
# - если передан аргумент 'local', тогда добавляет всех
# локальных обычных пользователей к ранее полученным и
# очищенным файлам, затем копирует их обратно на удалённый.
#--------------------------------------------------------
sub ProcessFiles {
my $which = shift;
my $tmpfile;
my %username = ();
my %usergroup = ();
my %userUID = ();
my %userGID = ();
my @info;
foreach my $f ('passwd','group','shadow','gshadow') {
my $tmpfile = "$tmpDir/$f.REMOTE";
if ($which eq 'remote') {
# Получаем удалённый файл
unlink $tmpfile if -e $tmpfile;
scp("$remoteHost:$systemConfigDir/$f", $tmpfile)
or die ("Не удалось получить '$f' от '$remoteHost'");
}
# Читаем содержимое файла
open CONFIGFILE, (($which eq 'remote') ? $tmpfile : "$systemConfigDir/$f");
my @lines = <CONFIGFILE>;
close CONFIGFILE;
# Открываем временный файл, либо обнуляя его, либо в режиме добавления
open TMPFILE, (($which eq 'remote') ? ">$tmpfile" : ">>$tmpfile" )
or die "Не удалось открыть файл '$tmpfile' для обработки";
foreach my $line (@lines) {
# Пропускаем комментарии, хотя они должны быть недопустимы в этих файлах
next if $f =~ /^\s*#/;
@info = (split ':', $line);
if ($f eq 'passwd') {
my $uid = $info[2];
my $isnormaluser = ($uid > $minUID) && ($uid < $maxUID);
next if (($which eq 'remote') ? $isnormaluser : !$isnormaluser);
$username{$info[0]} = TRUE;
$userUID{$uid} = TRUE;
$userGID{$info[3]} = TRUE;
} elsif ($f eq 'group') {
my $gid = $info[2];
my $isnormalgroup = ($gid > $minGID) && ($gid < $maxGID);
next if (($which eq 'remote') ? $isnormalgroup : !$isnormalgroup);
$usergroup{$info[0]} = TRUE;
} elsif ($f eq 'shadow') {
next if !exists $username{$info[0]};
} else {
next if !exists $usergroup{$info[0]};
}
# Любая строка, которая дошла до этого места, считается допустимой
print TMPFILE $line;
}
close TMPFILE;
if ($which eq 'local') {
# отправляем файл обратно
scp($tmpfile, "$remoteHost:$systemConfigDir/$f") or
die ("Не удалось отправить '$f' на '$remoteHost'");
unlink $tmpfile;
}
}
}
#--------------------------------------------------------
# Убедитесь, что мы чистим временные файлы при выходе
#--------------------------------------------------------
END {
my $tmpfile;
foreach my $f ('passwd','group','shadow','gshadow') {
$tmpfile = "$tmpDir/$f.REMOTE";
unlink $tmpfile if -e $tmpfile;
}
}
Обновление 21Май2010: обновлён код для улучшения синхронизации ID групп
Я использую rsync в записи crontab для простой резервной копии, которая выполняет ту же самую задачу. К сожалению, я не делаю это через ssh. Моя запись в crontab выглядит следующим образом:
0 4 * * 0 rsync -av –delete /etc/ /backup/etc/
На самом деле мой crontab сначала включает мой NAS-сервер, т.е. второй путь, который вы видите выше, с помощью wake-on-lan, а затем выполняет несколько резервных копий, и эта резервная копия является одной из них. Cron затем отправляет мне email, уведомляя, что было скопировано, т.е. какие файлы были синхронизированы и так далее.
Я не изучал вопрос выполнения этого через ssh, но, надеюсь, это поможет.
Ваше требование о частичной синхронизации значит, что скрипт должен быть намного сложнее, и, следовательно, более вероятно, что он будет иметь баги. Лично я бы взял время и изучил, насколько сложно было бы просто исправить эти другие учётные записи. Я не знаю, о сколько сервисах мы говорим, но я предположил бы, что все, что вам нужно будет сделать после изменения или настройки ID сервисов, это обновить владельцев некоторых файлов.
Если вы хотели бы сделать что-то действительно простое, вы могли бы настроить что-то вроде rdist (введение), чтобы просто отправлять файлы туда, куда вы хотите, на другой сервер. Чтобы это было безопасно, вам нужно будет настроить доступ на основе ключей SSH для процесса rdist.
.
Ответ или решение
Синхронизация списка паролей пользователей между серверами на базе CentOS (вариант дистрибутива RedHat) — это задача, которая может быть решена различными способами в зависимости от требований к безопасности, простоте и надежности. В данном контексте, рассматривается задача односторонней синхронизации списка пользователей с UID выше 500, без использования LDAP или NIS, и с возможностью подключения по SSH к резервному серверу.
Теория
Основной целью односторонней синхронизации является поддержание актуального состояния списка пользователей и групп на резервном сервере, что важно для обеспечения корректности и согласованности данных в случае отказа основного сервера. Стандартные инструменты, такие как LDAP или NIS, хотя и очень мощные, могут оказаться избыточными и сложными для конфигурации в небольших сценариях. В таких случаях можно воспользоваться скриптами для копирования необходимых записей из файлов /etc/passwd, /etc/shadow и /etc/group.
Пример
Возьмем предложенные в описании подходы: использование awk для фильтрации пользователей по UID и scp для передачи данных. Скрипт может выглядеть следующим образом:
awk -F: '($3>=500) && ($3!=65534)' /etc/passwd > /tmp/passwd.new
awk -F: '($3>=500) && ($3!=65534)' /etc/group > /tmp/group.new
awk -F: '($3>=500) && ($3!=65534) {print $1}' /etc/passwd | grep -f - /etc/shadow > /tmp/shadow.new
scp /tmp/passwd.new user@backupserver:/etc/passwd
scp /tmp/group.new user@backupserver:/etc/group
scp /tmp/shadow.new user@backupserver:/etc/shadow
Применение
-
Предварительная настройка:
- Установите беспарольный доступ по SSH от основного сервера к резервному. Это можно сделать, создав ключ SSH с помощью
ssh-keygen
и добавив его вauthorized_keys
на резервном сервере. - Убедитесь, что у вас есть доступ для записи в /etc каталоге на резервном сервере, возможно потребуется временно предоставить соответствующие права.
- Установите беспарольный доступ по SSH от основного сервера к резервному. Это можно сделать, создав ключ SSH с помощью
-
Создание скрипта: Создайте bash-скрипт, основанный на вышеуказанном примере. Это позволит автоматизировать процесс синхронизации.
-
Настройка расписания: Используйте cron или аналогичный инструмент для автоматического запуска скрипта. Например, добавьте следующую строку в crontab чтобы скрипт выполнялся ежедневно в полночь:
0 0 * * * /path/to/your/sync-script.sh
-
Безопасность и резервирование: Не забудьте о важности резервного копирования ваших критически важных данных. Перед применением изменений убедитесь, что у вас есть резервное копирование текущих версий файлов /etc/passwd, /etc/group, /etc/shadow как на основном, так и на резервном сервере.
Заключение
Выдающийся аспект этого решения заключается в том, что оно использует минимальные системные ресурсы и простое в реализации. Однако важно акцентировать внимание на безопасности. Развёртывание беспарольного доступа требует тщательной оценки рисков и соответствующих мер защиты резервного сервера, таких как настройка firewall и мониторинга попыток несанкционированного доступа.
Хотя в данном случае рассматривается простое решение с использованием bash-скриптов, в сложных инфраструктурах и при обработке большого количества пользователей можно рассмотреть более продвинутые решения, такие как автоматическое управление конфигурацией с помощью инструментов Ansible или Puppet, которые позволяют централизованно управлять настройками серверов.