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

Ren*_*uis 5 linux synchronization user-management

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

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

有什么建议吗?

编辑:
感谢到目前为止的建议,但我认为我没有让自己足够清楚。
我只考虑同步UID 为 500 或以上的普通用户。
两个系统上的系统/服务用户(UID 低于 500)可能不同。
所以恐怕你不能只同步整个文件。

gol*_*udo 10

您可以使用 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
Run Code Online (Sandbox Code Playgroud)

然后使用 rsync、scp 或您选择的文件传输方法将文件复制到您的备份系统。当您需要恢复这些文件时,可以将这些文件附加到“干净的”密码、组或影子文件的末尾(即:仅限默认系统用户/组,以防止无意中重复 ID/用户名)。

cat passwd.new >> /etc/passwd
cat group.new >> /etc/group
cat shadow.new >> /etc/shadow
Run Code Online (Sandbox Code Playgroud)


Ren*_*uis 2

好吧,我认为有一些现有的东西我可以使用,而不必推出自己的解决方案,但我必须快速做一些事情。

下面的脚本可以满足我的需要。

指示

要使其正常工作,只需更改被视为普通用户的最小和最大 UID 以及远程主机名或 IP 地址的几个配置变量即可。

您必须将远程服务器设置为接受来自本地服务器root用户的传入 SSH 会话,而无需输入密码。
Commander Keen在本页的回答中暗示了它是如何完成的,但您也可以参考无密码 SSH 登录以获取详细说明。

怎么运行的

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

下一步是检查passwdgroupShadowgshadow的每个本地版本,并将“普通”用户附加到相应的临时文件中,然后将它们上传回远程服务器以替换旧的。

警告

在尝试任何操作之前,请确保在本地和远程服务器上复制 passwdgroupShadowgshadow 。

安全

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

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