服务器之间简单的单向同步用户密码列表

服务器之间简单的单向同步用户密码列表

使用 RedHat 衍生发行版 (CentOS),我想将常规用户 (UID 超过 500) 和组 (以及影子文件) 的列表推送到备份服务器。
同步只是单向的,从主服务器到备份服务器。

我真的不想处理 LDAP 或 NIS。
我需要的只是一个可以每晚运行的简单脚本,以保持备份服务器更新。
主服务器可以通过 SSH 进入备份系统。

有什么建议吗?

编辑:
感谢到目前为止的建议,但我认为我没有表达清楚。
我只考虑同步普通用户,他们的UID 大于或等于 500
两个系统上的系统/服务用户(UID 低于 500)可能不同。
因此,恐怕您无法同步整个文件。

答案1

您可以使用 awk 提取 ID 为 500 或更大的用户/组。我还擅自排除了用户 ID 65534,该 ID 通常为“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 或您选择的文件传输方法将文件复制到您的备份系统。当您需要恢复这些文件时,可以将它们附加到“干净”的密码、组或影子文件的末尾(即:仅限默认系统用户/组,以防止 ID/用户名意外重复)。

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 登录了解详细说明。

怎么运行的

该脚本的作用是复制每个远程密码团体阴影阴影将远程服务器上的文件复制到本地服务器上的临时位置。
然后,它会从所有“普通”用户中删除这些临时文件,只保留对系统用户的引用。

下一步是研究每个本地版本的密码团体阴影阴影并将“普通”用户附加到其相应的临时文件中,然后将其中的每一个上传回远程服务器以替换旧文件。

警告

在尝试任何操作之前,请确保复制一份 密码团体阴影阴影在本地和远程服务器上。

安全

文件所有权和属性被保留。无论同步是否成功,
临时文件都会被保存和删除。 本地服务器必须能够无密码访问备份(但反之则不行)。这是必要的,这样我们才能获得用户帐户配置文件(否则会受到限制)。/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;
        }
}

2010 年 5 月 21 日更新:更新代码以改进组 ID 同步

相关内容