Простая односторонняя синхронизация списка паролей пользователей между серверами

Вопрос или проблема

Используя дистрибутив 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 написанных Гаем Маором.

Существует много способов и решений, но чтобы ответить на первоначальный вопрос, нужно выполнить три шага:

  1. Создайте SSH-ключ без пароля на сервере:

    ssh-keygen -b 4096

  2. Скопируйте .ssh/id_rsa.pub в .ssh/authorized__keys2 на клиенте:

    scp ~/.ssh/id_rsa.pub client:.ssh/authorized_keys2 

  3. Добавьте что-то вроде этого в ваш /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

Применение

  1. Предварительная настройка:

    • Установите беспарольный доступ по SSH от основного сервера к резервному. Это можно сделать, создав ключ SSH с помощью ssh-keygen и добавив его в authorized_keys на резервном сервере.
    • Убедитесь, что у вас есть доступ для записи в /etc каталоге на резервном сервере, возможно потребуется временно предоставить соответствующие права.
  2. Создание скрипта: Создайте bash-скрипт, основанный на вышеуказанном примере. Это позволит автоматизировать процесс синхронизации.

  3. Настройка расписания: Используйте cron или аналогичный инструмент для автоматического запуска скрипта. Например, добавьте следующую строку в crontab чтобы скрипт выполнялся ежедневно в полночь:

    0 0 * * * /path/to/your/sync-script.sh
  4. Безопасность и резервирование: Не забудьте о важности резервного копирования ваших критически важных данных. Перед применением изменений убедитесь, что у вас есть резервное копирование текущих версий файлов /etc/passwd, /etc/group, /etc/shadow как на основном, так и на резервном сервере.

Заключение

Выдающийся аспект этого решения заключается в том, что оно использует минимальные системные ресурсы и простое в реализации. Однако важно акцентировать внимание на безопасности. Развёртывание беспарольного доступа требует тщательной оценки рисков и соответствующих мер защиты резервного сервера, таких как настройка firewall и мониторинга попыток несанкционированного доступа.

Хотя в данном случае рассматривается простое решение с использованием bash-скриптов, в сложных инфраструктурах и при обработке большого количества пользователей можно рассмотреть более продвинутые решения, такие как автоматическое управление конфигурацией с помощью инструментов Ansible или Puppet, которые позволяют централизованно управлять настройками серверов.

Оцените материал
Добавить комментарий

Капча загружается...