Sincronización unidireccional simple de la lista de contraseñas de usuarios entre servidores

Sincronización unidireccional simple de la lista de contraseñas de usuarios entre servidores

Al utilizar una distribución derivada de RedHat (CentOS), me gustaría mantener la lista de usuarios habituales (UID superior a 500) y el grupo (y los archivos ocultos) enviados a un servidor de respaldo.
La sincronización es unidireccional, desde el servidor principal al servidor de respaldo.

Realmente no quiero tener que lidiar con LDAP o NIS.
Todo lo que necesito es un script simple que pueda ejecutarse todas las noches para mantener actualizado el servidor de respaldo.
El servidor principal puede ingresar mediante SSH al sistema de respaldo.

¿Cualquier sugerencia?

Editar:
Gracias por las sugerencias hasta ahora, pero creo que no fui lo suficientemente claro.
Sólo estoy pensando en sincronizar usuarios normales cuyosUID es igual o superior a 500.
Los usuarios del sistema/servicio (con UID inferior a 500) pueden ser diferentes en ambos sistemas.
Así que me temo que no puedes simplemente sincronizar todos los archivos.

Respuesta1

Puede utilizar awk para extraer usuarios/grupos con ID de 500 o más. También me he tomado la libertad de excluir el ID de usuario 65534, que a menudo está reservado para el usuario "nadie" (dependiendo de la distribución; no tengo idea si CentOS lo hace):

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

Luego use rsync, scp o el método de transmisión de archivos que prefiera para copiar los archivos a su sistema de respaldo. Luego, estos archivos se pueden agregar al final de un archivo de contraseña, grupo o sombra "limpio" cuando necesite restaurarlos (es decir, solo usuarios/grupos predeterminados del sistema, para evitar duplicaciones no intencionales de ID/nombre de usuario).

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

Respuesta2

NIS/NIS+ se inventaron exactamente por esta razón.

Pero son un poco feos y la autenticación centralizada (LDAP/Kerberos/SMB/etc.) es una idea mucho mejor si puedes hacerlo. Para configurar NIS/NIS+ necesitará:

Paquetes:

yp-tools ypbind ypserv portmap

y un /etc/yp.conf con algo como:

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

y luego en /etc/sysconfig/network:

NISDOMAIN=example.org

Y me volví vago, aquí tienes un buen cómo hacerlo:http://www.wains.be/index.php/2007/02/28/setting-up-nis-under-centos-4/eso te guiará a través de él.

Personalmente, para la copia de seguridad, simplemente haría una copia de seguridad de todo el directorio /etc/ y terminaría con ello. Son sólo unos pocos megas como mucho.

Respuesta3

Utilice cppw y 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.

Respuesta4

Bueno, pensé que existía algo que podía usar sin tener que implementar mi propia solución, pero tenía que hacer algo rápido.

A continuación se muestra un script que hará justo lo que necesitaba.

Instrucciones

Para que funcione, simplemente cambie las pocas variables de configuración para que el UID mínimo y máximo se considere como unnormalusuario y el nombre del host remoto o la dirección IP.

Debe haber configurado el servidor remoto para aceptar sesiones SSH entrantes del rootusuario del servidor local sin tener que ingresar una contraseña.
Comandante Keeninsinuó cómo se hace en su respuesta en esta página, pero también puede consultarinicio de sesión SSH sin contraseñapara obtener instrucciones detalladas.

Cómo funciona

Lo que hace el script es copiar cada uno de los mandoscontraseña,grupo,sombra,sombraarchivos desde el servidor remoto a una ubicación temporal en el servidor local.
Luego elimina estos archivos temporales de todos los usuarios "normales", manteniendo sólo las referencias a los usuarios del sistema.

El siguiente paso es revisar cada una de las versiones locales decontraseña,grupo,sombra,sombray agregar solo los usuarios "normales" a sus archivos temporales correspondientes, luego cargar cada uno de ellos nuevamente al servidor remoto para reemplazar el anterior.

Advertencia

Antes de intentar cualquier cosa, asegúrese de hacer una copia de su contraseña,grupo,sombra,sombratanto en el servidor local como en el remoto.

Seguridad

Se conservan la propiedad y los atributos del archivo.
Los archivos temporales se guardan /tmpy eliminan, independientemente de si la sincronización se realizó correctamente o no.
El servidor local debe tener acceso sin contraseña roota la copia de seguridad (pero no al revés). Esto es necesario para que podamos obtener los archivos de configuración de las cuentas de usuario (que de otro modo estarían restringidos).

El código

Este es un primer intento y es un poco complicado (no es un código bonito), pero hace el trabajo bastante bien y alguien más puede encontrarlo útil.

Es un script Perl que sólo depende delNet::SCPMódulo para copiar archivos de forma segura 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;
        }
}

Actualización 21MAY2010: código actualizado para mejorar la sincronización del ID del grupo

información relacionada