Sincronização unidirecional simples da lista de senhas de usuários entre servidores

Sincronização unidirecional simples da lista de senhas de usuários entre servidores

Usando uma distribuição derivada do RedHat (CentOS), gostaria de manter a lista de usuários regulares (UID acima de 500) e grupos (e arquivos shadow) enviados para um servidor de backup.
A sincronização é unidirecional, do servidor principal para o servidor de backup.

Eu realmente não quero ter que lidar com LDAP ou NIS.
Tudo que preciso é de um script simples que possa ser executado todas as noites para manter o servidor de backup atualizado.
O servidor principal pode usar SSH no sistema de backup.

Alguma sugestão?

Editar:
Obrigado pelas sugestões até agora, mas acho que não fui claro o suficiente.
Estou apenas procurando sincronizar usuários normais cujosUID é igual ou superior a 500.
Os usuários do sistema/serviço (com UID abaixo de 500) podem ser diferentes em ambos os sistemas.
Então você não pode simplesmente sincronizar os arquivos inteiros, infelizmente.

Responder1

Você pode usar o awk para extrair usuários/grupos com IDs de 500 ou mais. Também tomei a liberdade de excluir o ID do usuário 65534, que geralmente é reservado para o usuário "ninguém" (dependendo da distribuição; não tenho ideia se o CentOS faz isso):

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

Em seguida, use rsync, scp ou o método de transmissão de arquivos de sua preferência para copiar os arquivos para o sistema de backup. Esses arquivos podem então ser anexados ao final de uma senha, grupo ou arquivo shadow 'limpo' quando você precisar restaurá-los (ou seja: apenas usuários/grupos padrão do sistema, para evitar duplicações não intencionais de ID/nome de usuário).

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

Responder2

NIS/NIS+ foram inventados exatamente por esse motivo.

Mas eles são meio feios e centralizados (LDAP/Kerberos/SMB/etc.). A autenticação é uma idéia muito melhor se você puder fazê-lo. Para configurar o NIS/NIS+ você precisará de:

Pacotes:

yp-tools ypbind ypserv portmap

e um /etc/yp.conf com algo como:

domain example.org server nis.example.org
ypserver nis.example.org

e depois em /etc/sysconfig/network:

NISDOMAIN=example.org

E fiquei com preguiça, aqui está um bom tutorial:http://www.wains.be/index.php/2007/02/28/setting-up-nis-under-centos-4/isso irá orientá-lo.

Pessoalmente, para backup, eu apenas faria backup de todo o diretório /etc/ e terminaria com ele. São apenas alguns megas, no máximo.

Responder3

use cppw e 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.

Responder4

Bem, pensei que havia algo que eu pudesse usar sem ter que criar minha própria solução, mas precisava fazer algo rápido.

Abaixo está um script que fará exatamente o que eu precisava.

Instruções

Para que funcione, basta alterar algumas variáveis ​​de configuração para que o UID mínimo e máximo seja considerado como umnormalusuário e o nome do host remoto ou endereço IP.

Você deve ter configurado o servidor remoto para aceitar sessões SSH recebidas do usuário do servidor local rootsem precisar inserir uma senha.
Comandante Keensugeriu como isso é feito em sua resposta nesta página, mas você também pode consultarlogin SSH sem senhapara obter instruções detalhadas.

Como funciona

O que o script faz é copiar cada um dos controles remotossenha,grupo,sombra,sombraarquivos do 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.

A próxima etapa é passar por cada uma das versões locais dosenha,grupo,sombra,sombrae anexar apenas os usuários "normais" aos arquivos temporários correspondentes e, em seguida, enviar 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 senha,grupo,sombra,sombranos servidores locais e remotos.

Segurança

A propriedade e os atributos do arquivo são preservados.
Os arquivos temporários são salvos /tmpe excluídos, independentemente de a sincronização ter sido bem-sucedida ou não.
O servidor local deve ter acesso ao backup sem senha root(mas não o contrário). Isso é necessário para que possamos obter os arquivos de configuração das contas de usuário (que de outra forma seriam restritos).

O código

Esta é uma primeira tentativa e é um pouco confusa (não é um código bonito), mas funciona muito bem e outra pessoa pode achar útil.

É um script Perl que depende apenas doNet::SCPmó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

informação relacionada