Bem, eu pensei que existia algo que eu pudesse usar sem ter que rolar minha própria solução, mas eu tive que fazer algo rápido.
Abaixo está um script que fará exatamente o que eu precisava.
Instruções
Para que funcione, apenas altere as poucas variáveis de configuração para o UID mínimo e máximo a ser considerado como um usuário normal e o nome do host remoto ou endereço IP.
Você deve configurar o servidor remoto para aceitar as sessões SSH de entrada do usuário root
do servidor local sem precisar digitar uma senha.
Commander Keen insinuou como é feito em sua resposta nesta página, mas você também pode se referir a login SSH sem senha para detalhes instruções.
Como funciona
O que o script faz é copiar cada um dos arquivos passwd , grupo , sombra , gshadow do o servidor remoto para um local temporário no servidor lcoal.
Em seguida, ele remove esses arquivos temporários de todos os usuários "normais", mantendo apenas as referências aos usuários do sistema.
O próximo passo é passar por cada uma das versões locais de passwd , grupo , sombra , gshadow e anexando apenas os usuários "normais" aos seus arquivos temporários correspondentes e, em seguida, fazendo o upload de cada um deles de volta ao servidor remoto para substituir o antigo.
Aviso
Antes de tentar qualquer coisa, certifique-se de fazer uma cópia do seu passwd , grupo , sombra , gshadow nos servidores locais e remotos.
Segurança
A propriedade e os atributos dos arquivos são preservados.
Os arquivos temporários são salvos em /tmp
e excluídos, independentemente de a sincronização ter sido bem-sucedida ou não.
O servidor local deve ter root
de acesso sem senha ao backup (mas não o contrário). Isso é necessário para que possamos obter os arquivos de configuração de contas de usuários (que são restritos).
O código
Esta é uma primeira tentativa e é um pouco confusa (não é um código bonito), mas faz o trabalho muito bem e outra pessoa pode achar útil.
É um script em Perl que só depende do Net::SCP
módulo para copiar arquivos com segurança entre servidores.
#!/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;
}
}
Atualização 21MAY2010: código atualizado para melhorar a sincronização do ID do grupo