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

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

Используя дистрибутив на основе RedHat (CentOS), я хотел бы сохранить список обычных пользователей (UID более 500) и групп (и теневых файлов), отправленных на резервный сервер.
Синхронизация только односторонняя, с основного сервера на резервный сервер.

Я не хочу иметь дело с LDAP или NIS.
Все, что мне нужно, это простой скрипт, который можно запускать каждую ночь, чтобы обновлять резервный сервер.
Основной сервер может подключаться к резервной системе по SSH.

Любое предложение?

Редактировать:
Спасибо за предложения, но я думаю, что я недостаточно ясно выразился.
Я рассматриваю только синхронизацию обычных пользователей, чьиUID равен или превышает 500.
Пользователи системы/службы (с UID ниже 500) могут быть разными в обеих системах.
Так что, боюсь, вы не сможете просто синхронизировать все файлы.

решение1

Вы можете использовать awk для извлечения пользователей/групп с идентификаторами 500 или выше. Я также взял на себя смелость исключить идентификатор пользователя 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, когда вам нужно будет их восстановить (т. е. только для пользователей/групп системы по умолчанию, чтобы предотвратить непреднамеренное дублирование идентификатора/имени пользователя).

cat passwd.new >> /etc/passwd
cat group.new >> /etc/group
cat shadow.new >> /etc/shadow

решение2

Именно по этой причине были изобретены 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/ и все. Это всего несколько мегабайт максимум.

решение3

используйте cppw и cpgr:

CPPW(8)                                                                                                                                                      

NAME
       cppw, cpgr - copy with locking the given file to the 
       password or group file

SYNOPSIS<br>
       cppw [-h] [-s] password_file
       cpgr [-h] [-s] group_file

DESCRIPTION
       cppw  and  cpgr will copy, with locking, the given file to
       /etc/passwd and /etc/group, respectively.  With the -s flag, 
       they will copy the shadow versions of those files, 
       /etc/shadow and /etc/gshadow, respectively.

       With the -h flag, the commands display a short help message
       and exit silently.

SEE ALSO
       vipw(8), vigr(8), group(5), passwd(5), shadow(5), gshadow(5)

AUTHOR
       cppw and cpgr were written by Stephen Frost, based on vipw 
       and vigr written by Guy Maor.

решение4

Ну, я думал, что есть что-то существующее, что я мог бы использовать без необходимости разрабатывать собственное решение, но мне нужно было что-то сделать быстро.

Ниже представлен скрипт, который сделает именно то, что мне нужно.

инструкции

Чтобы это работало, просто измените несколько переменных конфигурации, чтобы минимальный и максимальный UID считалисьнормальныйпользователь и имя удаленного хоста или IP-адрес.

Вам необходимо настроить удаленный сервер на прием входящих сеансов SSH от rootпользователя локального сервера без необходимости ввода пароля.
Командир Киннамекнул на то, как это делается, в своем ответе на этой странице, но вы также можете сослаться навход по SSH без паролядля получения подробных инструкций.

Как это работает

Скрипт копирует каждый из удаленныхпароль,группа,тень,gshadowфайлы с удаленного сервера во временное местоположение на сервере lcoal.
Затем он удаляет эти временные файлы у всех "обычных" пользователей, оставляя только ссылки на системных пользователей.

Следующий шаг — пройтись по каждой локальной версиипароль,группа,тень,gshadowи добавляя только «обычных» пользователей в соответствующие им временные файлы, а затем загружая каждого из них обратно на удаленный сервер для замены старого.

Предупреждение

Прежде чем что-либо предпринять, обязательно сделайте копию пароль,группа,тень,gshadowкак на локальном, так и на удаленном сервере.

Безопасность

Владение файлами и атрибуты сохраняются.
Временные файлы сохраняются /tmpи удаляются, независимо от того, была ли синхронизация успешной или нет.
Локальный сервер должен иметь беспарольный rootдоступ к резервной копии (но не наоборот). Это необходимо, чтобы мы могли получить файлы конфигурации учетных записей пользователей (которые в противном случае ограничены).

Код

Это первая попытка, и она немного запутанная (код некрасивый), но она неплохо справляется со своей задачей и может оказаться полезной для кого-то еще.

Это скрипт Perl, который зависит только отNet::SCPмодуль для безопасного копирования файлов между серверами.

#!/usr/bin/perl -w
use Net::SCP qw(scp);
use strict;

use constant TRUE  => (1==1);
use constant FALSE => (1==0);

#--------------------------------------------------------
# Configuration
# Modify as needed
#--------------------------------------------------------
my $remoteHost = '10.13.113.2';  # email backup server
my $minUID     = 500;
my $maxUID     = 30000;
my $minGID     = 500;
my $maxGID     = 30000;

#--------------------------------------------------------
# Internal variables, normally not to be modified.
#--------------------------------------------------------
my $systemConfigDir = '/etc';
my $tmpDir = $ENV{TMPDIR} || $ENV{TMP} || $ENV{TEMP} || '/tmp';

#--------------------------------------------------------
#  Main
#--------------------------------------------------------
# STEP 1
# Get the remote files to /tmp and
# clean them of their normal users
ProcessFiles('remote');

# STEP 2
# Append the local normal users to the temp files
# and then send them back to the remote
ProcessFiles('local');

#--------------------------------------------------------
# ProcessFiles sub does one of two things:
# - if the passed argument is 'remote', then fetch each
#   user account file from the remote server, then remove
#   all normal users from each file, only keeping the
#   system users.
# - if the passed argument is 'local', then appends all
#   normal local users to the previously fetched and
#   cleaned-up files, then copies them back to the remote.
#--------------------------------------------------------
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') {
                        # Fetch the remote file
                        unlink $tmpfile if -e $tmpfile;
                        scp("$remoteHost:$systemConfigDir/$f", $tmpfile)
                                or die ("Could not get '$f' from '$remoteHost'");
                }
                # Glob the file content
                open CONFIGFILE, (($which eq 'remote') ? $tmpfile : "$systemConfigDir/$f");
                my @lines = <CONFIGFILE>;
                close CONFIGFILE;
                # Open the temp file, either truncating it or in append mode
                open TMPFILE,  (($which eq 'remote') ? ">$tmpfile" : ">>$tmpfile" )
                        or die "Could not open '$tmpfile' for processing";
                foreach my $line (@lines) {
                         # Skip comments, although they should be illegal in these files
                        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]};
                        }
                        # Any line that reaches this point is valid
                        print TMPFILE $line;
                }
                close TMPFILE;
                if ($which eq 'local') {
                        # send the file back
                        scp($tmpfile, "$remoteHost:$systemConfigDir/$f") or
                                die ("Could not send '$f' to '$remoteHost'");
                        unlink $tmpfile;
                }
        }
}

#--------------------------------------------------------
# Make sure we cleanup the temp files when we exit
#--------------------------------------------------------
END {
        my $tmpfile;
        foreach my $f ('passwd','group','shadow','gshadow') {
                $tmpfile = "$tmpDir/$f.REMOTE";
                unlink $tmpfile if -e $tmpfile;
        }
}

Обновление 21 МАЯ 2010: обновлен код для улучшения синхронизации идентификатора группы

Связанный контент