ホストAとBの間でユーザーUID> 1000のパスワードハッシュを同期します。

ホストAとBの間でユーザーUID> 1000のパスワードハッシュを同期します。

2つのCentOSシステムがあり、/etc/shadowシステムA(ローカル)からシステムB(リモート)として保存されたパスワードハッシュのみを同期しようとしています。ただし、両方のシステムに存在するUID> 1000のユーザーに対してのみ(UIDではなくユーザー名に基づいて)、AとBの同じユーザー名に対してUIDが異なる場合があります。

rsync、LDAP、NIS などのソリューションは使用できません。また、このシステムではUIDが1000未満のアカウントに触れることはできません。

ホストAとBのユーザーUIDが異なる可能性があるため、AからBにパスワードハッシュを同期させるには、次のことが重要です。 (1)ユーザー名は両方のシステムに存在する必要があります。 (2)ユーザー名のUIDは1000より大きくなければなりません(異なる場合があります)システムAとシステムB

良いPerlスクリプトが見つかりました。ルノー Bumpuisこれを行うには、私の要件にいくつかの調整が必要になる可能性があり、変更しないで/etc/passwdください/etc/group。私はPerlプログラマーではないので、ここに助けを求めます。よろしくお願いします。

#!/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;
        }
}

ベストアンサー1

join@roaiamaのコマンドの使用に基づいて、この答えはgetentpasswdファイルとシャドウファイルを直接読み取り、リモートchpasswdホストでパスワードを変更する代わりにインポートすることです。

パスワード変更コードは簡単ですが、古いシャドウエントリのバックアップコピーを作成するのはリモートホストでも使用されるchpasswdため、少し複雑です。getent shadow

join -t : -j 1 -o 2.{1..2} \
    <(getent passwd | awk -F: '$3 > 1000 {print $1}' | sort) \
    <(getent shadow | sort) | 
  ssh remotehost 'umask 0027 &&
    getent shadow > /etc/shadow.old &&
    chgrp shadow /etc/shadow.old &&
    chpasswd -e 2>/dev/null'

最初の2つのフィールド、ユーザー名と暗号化されたパスワード(出力形式は1行に1つのユーザー名:パスワードのペア)のみをsshに送信します。古いシャドウファイルのバックアップコピーを作成した後、標準入力chpasswdで指定されたパスワードを変更するためにリモートシェルが実行されます。

この-eオプションは、chpasswdパスワードが暗号化されたことを示します。このオプションがない場合は、提供されたパスワードを再暗号化します。

chpasswdリモートシステムに存在しないユーザー名はstderrでエラーが発生しますが、存在するユーザー名のパスワードは変更され続けます。 chpasswd上記のようにstderrを/ dev / nullにリダイレクトできます。

注:他のエラーは引き続き表示されますが、予期し、無害な「ユーザー名は存在しません」エラーのみを削除するスクリプトにstderrをパイプすることをお勧めします。私のテストVMに存在しないユーザーのエラー出力はchpasswd次のとおりです。

# printf '%s\n' "foo:bar" "xyzzy:fool" | chpasswd
chpasswd: (user foo) pam_chauthtok() failed, error:
Authentication token manipulation error
chpasswd: (line 1, user foo) password not changed
chpasswd: (user xyzzy) pam_chauthtok() failed, error:
Authentication token manipulation error
chpasswd: (line 2, user xyzzy) password not changed

おすすめ記事