21 dic 2014

IBM i (iSeries / AS/400) - Haciendo respaldos en la nube/red (Cloud Backup) - Versión segura usando SCP

Esta versión es similar al post anterior de "http://diego-k.blogspot.com/2014/12/ibm-i-iseries-as400-haciendo-respaldos.html" pero es algo más simple (es un sólo CL) y según mis pruebas incluso es más veloz.

En lugar de armar un script de FTP, simplemente usa SCP para transferir archivos. Esto permite hacer una conexión sin depender de una password, la información viaja de forma segura, y el archivo es encriptado antes de ser enviado.

Necesitaremos tener instalado el 5733-SC1 (Portable Utilities) para contar con el comando SCP, un servidor que soporte SSH donde enviaremos el archivo, y la generación de la clave pública y clave privada para transferir sin necesidad de password.

El CLLE es un poco extenso, pero simple de seguir.

Los respaldos se hacen completos el primer día del mes y los lunes, e incrementales el resto de los días.
Se requiere crear la DTAARA BKP7ZPSWD con la llave de encripción. En este caso es una llave de 64 caracteres.

También necesitaremos el archivo BKPLIB que tiene la estructura de la salida de DSPOBJD pero en ODOBNM tendremos la lista de bibliotecas a respaldar.

Obviamente el servidor SSH debe ser visto desde el servidor IBM i (iSeries) donde tengamos el script.

             PGM
             DCL        VAR(&ENCKEY) TYPE(*CHAR) LEN(64)
             DCL        VAR(&zipCMD) TYPE(*CHAR) LEN(256)
             DCL        VAR(&SSHCMD) TYPE(*CHAR) LEN(256)
             DCL        VAR(&SCPCMD) TYPE(*CHAR) LEN(256)
             dcl        var(&FECHA) type(*CHAR) LEN(8)
             DCL        var(&HOY) type(*CHAR) LEN(8)
             DCL        VAR(&DIA)  TYPE(*CHAR) LEN(2)
             DCL        VAR(&NDIA) TYPE(*DEC) LEN(2 0)
             DCL        VAR(&MES)  TYPE(*CHAR) LEN(2)
             DCL        VAR(&ANIO)  TYPE(*CHAR) LEN(2)
             DCL        VAR(&DOW) TYPE(*CHAR) LEN(4)
             DCLF       FILE(MYLIB/BKPLIB)
             RTVDTAARA  DTAARA(MYLIB/BKP7ZPSWD *ALL) +
                          RTNVAR(&ENCKEY)
             RTVSYSVAL  SYSVAL(QDAYOFWEEK) RTNVAR(&DOW)
             RTVSYSVAL  SYSVAL(QDAY) RTNVAR(&DIA)
             RTVSYSVAL  SYSVAL(QMONTH) RTNVAR(&MES)
             RTVSYSVAL  SYSVAL(QYEAR) RTNVAR(&ANIO)
             CHGVAR     VAR(&FECHA) VALUE('01' *TCAT &MES *TCAT +
                          '20' *TCAT &ANIO)
             CHGVAR     VAR(&NDIA) VALUE(&DIA)
             CHGVAR     VAR(&HOY) VALUE( '20' *TCAT &ANIO *TCAT +
                        &MES *TCAT &DIA)
             CRTLIB LIB(BACKUPSAV) TEXT('Biblioteca de Backup')
             MONMSG     MSGID(CPF2111)
             CLRLIB     LIB(BACKUPSAV)
             /* Borro los archivos  SAVF y 7z que hayan quedado */
             QSH        CMD('/QOpenSys/usr/bin/sh -c " +
                        rm /home/backupsav/*.7z"')
             QSH        CMD('/QOpenSys/usr/bin/sh -c " +
                        rm /home/backupsav/*.savf"')
             /* Creo el archivo  SAVF con perfiles de usuarios */
             CRTSAVF    FILE(BACKUPSAV/SAVSECDTA) TEXT('Seguridad')
             SAVSECDTA  DEV(*SAVF) SAVF(BACKUPSAV/SAVSECDTA) +
                          DTACPR(*NO) COMPACT(*NO)
             /* Muevo el archivo  SAVF al IFS */
             QSH        CMD('/QOpenSys/usr/bin/sh -c " +
                          mv /QSYS.LIB/BACKUPSAV.LIB/SAVSECDTA.FILE +
                          /home/backupsav/SAVSECDTA.savf"')
             /* Comprimo el archivo  SAVF */
             CHGVAR     VAR(&zipCMD) VALUE('/QOpenSys/usr/bin/sh -c " +
                         7za a -p' *tcat +
                         &ENCKEY *BCAT '/home/backupsav/savsecdta.7z +
                           /home/backupsav/savsecdta.savf"')

             QSH        CMD(&ZIPCMD)
             /* Borro el archivo  SAVF */
             QSH        CMD('/QOpenSys/usr/bin/sh -c " +
                        rm /home/backupsav/SAVSECDTA.savf"')
             MONMSG     MSGID(CPF0000)
             /* Creo el directorio remoto con la fecha */
             CHGVAR     VAR(&SSHCMD) VALUE('ssh -p 443 -i +
                          /home/ituser1/.ssh/id_rsa +
                          ituser1@mysshserver.com "mkdir +
                          -p /mnt/USB1/BACKUP/'  +
                          *TCAT &HOY *TCAT '"')
             QSH        CMD(&SSHCMD)
             MONMSG     MSGID(CPF0000)
             /* Transfiero el archivo en background */
             CHGVAR     VAR(&SCPCMD) VALUE('scp -P 443 -i +
                          /home/ituser1/.ssh/id_rsa +
                          /home/backupsav/savsecdta.7z +
                          ituser1@mysshserver.com:/mnt/USB1/BA+
                          CKUP/' *TCAT &HOY *TCAT '/ &')
             QSH        CMD(&SCPCMD)
             MONMSG     MSGID(CPF0000)
 LEE:        RCVF
             MONMSG     MSGID(CPF0864) EXEC(GOTO CMDLBL(FIN))
             CRTSAVF    FILE(BACKUPSAV/&ODOBNM) TEXT(&ODOBTX)
             MONMSG     MSGID(CPF0000)
             IF         COND((&DOW  = '*MON') *OR (&NDIA=1)) THEN( +
                        SAVLIB LIB(&ODOBNM) +
                          DEV(*SAVF) SAVF(BACKUPSAV/&ODOBNM) +
                          SAVACT(*LIB) ACCPTH(*YES) DTACPR(*NO) +
                          COMPACT(*NO) )
             ELSE   CMD(   +
                 SAVCHGOBJ  OBJ(*ALL) LIB(&ODOBNM) DEV(*SAVF) +
                          REFDATE(&FECHA) REFTIME(000000) +
                          SAVF(BACKUPSAV/&ODOBNM) SAVACT(*LIB) +
                          ACCPTH(*YES) DTACPR(*NO) COMPACT(*NO))


             /* MUEVO el archivo  SAVF al IFS */
             CHGVAR     VAR(&ZIPCMD) VALUE('/QOpenSys/usr/bin/sh -c " +
                          mv /QSYS.LIB/BACKUPSAV.LIB/' *TCAT &ODOBNM +
                          *TCAT '.FILE /home/backupsav/' *TCAT +
                          &ODOBNM *TCAT '.savf"')

             QSH        CMD(&zipCMD)
             /* Comprimo con 7zip y clave el archivo  SAVF */
             CHGVAR     VAR(&ZIPCMD) VALUE('/QOpenSys/usr/bin/sh -c " +
                        7za a -p' *tcat +
                        &ENCKEY *BCAT '/home/backupsav/' *TCAT +
                        &ODOBNM *TCAT '.7z /home/backupsav/' *TCAT +
                        &ODOBNM *TCAT '.savf"')

             QSH        CMD(&zipCMD)
             /* Borro el archivo  SAVF */
             CHGVAR     VAR(&ZIPCMD) VALUE('/QOpenSys/usr/bin/sh -c " +
                        rm /home/backupsav/' +
                        *tcat &ODOBNM *TCAT '.savf"')
             QSH        CMD(&zipCMD)
             /* Transfiero el archivo en background */
             CHGVAR     VAR(&SCPCMD) VALUE('scp -P 443 -i +
                          /home/ituser1/.ssh/id_rsa +
                          /home/backupsav/' *TCAT &ODOBNM *TCAT '.7z +
                          ituser1@mysshserver.com:/mnt/USB1/BA+
                          CKUP/' *TCAT &HOY *TCAT '/ &')
             QSH        CMD(&SCPCMD)
             MONMSG     MSGID(CPF0000)

             GOTO       CMDLBL(LEE)
 FIN:        ENDPGM

Espero que sea de utilidad

IBM i - Listando journals de auditoría usando PHP

Los journals de auditoría son bastante útiles a la hora de dar seguimiento a actividad de los usuarios. Para poder activar la auditoría del IBM i es necesario :

1) Crear un journal receiver y el journal, que es el que permitirá guardar la información. Es aconsejable respaldar los journals con cierta frecuencia para poder depurarlos. Lo ideal es respaldarlos de forma separada a los datos del equipo, por lo que recomienda crear una biblioteca especialmente para ellos

CRTLIB JRNLIB
RTJRNRCV JRNRCV(JRNLIB/AUD)
CRTJRN JRN(QSYS/QAUDJRN) JRNRCV(JRNLIB/AUD)   

2) Cambiar los valores del sistema para que se active la recogida de información

WRKSYSVAL y cambiar los siguientes valores

QAUDCTL
 *AUDLVL 
*OBJAUD 
*NOQTEMP

QAUDLVL
*AUTFAIL
*CREATE 
*DELETE 
*SECURITY
*SAVRST 

3) Marcar los usuarios que deseemos como auditables. Lo mismo podemos hacer con los objetos

La información se irá recogiendo en los receivers, pero no será muy util si no la podemos leer de forma cómoda.
Para tal fin, y pensando en poder armar reportes de auditoría, creé un par de programas en PHP que permiten listar estos informes.

Armé un pequeño CL que diariamente lista los incidentes de seguridad de AF (Fallas de autorización) y PW (Fallas de usuario y/o password) y guarda la información en una tabla

PGM                                                                   
                                                                      
             DCL        VAR(&FECHA1) TYPE(*CHAR) LEN(6)               
             DCL        VAR(&FECHA2) TYPE(*CHAR) LEN(8)               
             RTVSYSVAL  SYSVAL(QDATE)  RTNVAR(&FECHA1)                
             CVTDAT    DATE(&FECHA1) TOVAR(&FECHA2) FROMFMT(*sysval) +
                          TOFMT(*DMY) TOSEP(*NONE)                    
             chgvar     (&FECHA2)  Value(%SST(&FECHA2 1 4) *TCAT +    
                        '20' *TCAT %SST(&FECHA2 5 2))                 
             SNDMSG     MSG(&FECHA2) TOUSR(BOMIAUD)                   
             CPYAUDJRNE ENTTYP(AF PW CD) OUTFILE(MYLIB/QAUDIT) +  
                          OUTMBR(*FIRST *ADD) JRNRCV(*CURCHAIN) +     
                          FROMTIME(&FECHA2 000000)                    
             MONMSG     MSGID(CPF0000)                                
ENDPGM


Este pequeño CL lo agregué en el JOB SCHEDULLER para que diariamente ejecute en las noches y vaya poblando mis tablas de trabajo, que luego listaré en los reportes.

Luego,armé 2 programas en PHP, cada uno lista un tipo de entrada

AUDAF.php

<html>
<head>
<title>Journal de auditoria - Fallas de Autorizacion</title>
</head>
<p align="center"><b>Journal de auditoria - Fallas de Autorizacion</b></p>
<p>

<i><b>
<?php
include 'Connection.php';

$date = new DateTime();
$cdate = $date->format('Y-m-d');
$fdd = $date;
$fht = $date;
$cdd = $cdate . ' 00:00:00';
$cht = $cdate . ' 23:59:59';
if (!empty($_POST)){
    $fdesde=$_POST['fdesde'];
    $fhasta=$_POST['fhasta'];
} else {
    $fdesde=$cdate;
    $fhasta=$cdate;
}
echo "<form name='form1'' method='post'' action='audaf.php'>";
echo "<td>";
echo "<table width='100%'' border='0'' cellpadding='3'' cellspacing='1'' bgcolor='#FFFFFF'>";
echo "<tr>";
echo "<td colspan='3'><strong>Parametros del reporte</strong></td>";
echo "</tr>";
echo "<tr>";
echo "<td width='78'>Desde</td>";
echo "<td width='6'>:</td>";
echo "<td width='294'><input name='fdesde' type='text' id='fdesde' value='$fdesde'</td> </tr>";
echo "<tr> <td>Hasta</td><td>:</td>";
echo "<td><input name='fhasta'' type='text' id='fhasta' value='$fhasta' </td></tr>";
echo "<tr>";
echo "<td>&nbsp;</td>";
echo "<td>&nbsp;</td>";
echo "<td><input type='submit'' name='Submit'' value='Aceptar'></td>";
echo "</tr>";
echo "</table>";
echo "</td>";
echo "</form>";

if (!empty($_POST)){
$fdesde=$_POST['fdesde'];
$fhasta=$_POST['fhasta'];
$cdd = $fdesde . ' 00:00:00';
$cht = $fhasta . ' 23:59:59';
}

#Consulta  de registros PW
$sqlaf = "SELECT date(a.AFTSTP) as Fecha, Time(a.AFTSTP) as Hora,a.AFUSER as Usuario, a.AFJOB as Trabajo,a.AFNBR as Nro_Trabajo,a.AFPGM as Programa, a.AFPGMLIB as Biblioteca,trim(a.AFRADR) as Direccion_IP, a.AFVIOL as Tipo, b.AFVIOLDS as Descripcion
        From MYLIB.QAUDITAF a left outer join MYLIB.AUDAFTYPE b
        on a.AFVIOL = b.AFVIOL
        where a.AFTSTP >= '$cdd' and a.AFTSTP <=  '$cht'
        order by a.AFTSTP" ;

#echo $sqlaf;
$result=odbc_exec($conn,$sqlaf);


echo '<table border=1 cellspacing=0 cellpadding=2 ><tr>';
    $fila = 1;
     while (odbc_fetch_row($result)) {
         $FieldQ = odbc_num_fields($result);

         if ($fila==1) {
             for ($i = 1; $i <= $FieldQ ; $i++)
             {
             $fn = odbc_field_name($result,$i);
             echo "<th bgcolor=#AFAFAF>$fn</th>";
             }
             echo "</tr>";
         }
         $fila++;
        echo "<tr>";
         $td = "<td bgcolor=#DCEBF9>";
         if (($fila/2)==intval($fila/2)) {
             $td = "<td bgcolor=#FFFFFF>";
         }

         for ($i = 1; $i <= $FieldQ ; $i++)
         {
         $fn = odbc_field_name($result,$i);
         $Campo = odbc_result($result,$fn);
         echo $td . $Campo . "</td>";
         }
         echo "</tr>";

    }
    echo "</table>";

$clo = odbc_close($conn);
?></b></i>

//-------------------------------------------------------------//


Luego tengo otro programa que lista las entradas de fallas de usuario y password

AUDPW.php

<html>
<head>
<title>Journal de auditoria - Fallas de Password</title>
</head>
<p align="center"><b>Journal de auditoria - Fallas de Password</b></p>
<p>

<i><b>
<?php
include 'Connection.php';

$date = new DateTime();
$cdate = $date->format('Y-m-d');
$fdd = $date;
$fht = $date;
$cdd = $cdate . ' 00:00:00';
$cht = $cdate . ' 23:59:59';
if (!empty($_POST)){
    $fdesde=$_POST['fdesde'];
    $fhasta=$_POST['fhasta'];
} else {
    $fdesde=$cdate;
    $fhasta=$cdate;
}
echo "<form name='form1'' method='post'' action='audpw.php'>";
echo "<td>";
echo "<table width='100%'' border='0'' cellpadding='3'' cellspacing='1'' bgcolor='#FFFFFF'>";
echo "<tr>";
echo "<td colspan='3'><strong>Parametros del reporte</strong></td>";
echo "</tr>";
echo "<tr>";
echo "<td width='78'>Desde</td>";
echo "<td width='6'>:</td>";
echo "<td width='294'><input name='fdesde' type='text' id='fdesde' value='$fdesde'</td> </tr>";
echo "<tr> <td>Hasta</td><td>:</td>";
echo "<td><input name='fhasta'' type='text' id='fhasta' value='$fhasta' </td></tr>";
echo "<tr>";
echo "<td>&nbsp;</td>";
echo "<td>&nbsp;</td>";
echo "<td><input type='submit'' name='Submit'' value='Aceptar'></td>";
echo "</tr>";
echo "</table>";
echo "</td>";
echo "</form>";

if (!empty($_POST)){
$fdesde=$_POST['fdesde'];
$fhasta=$_POST['fhasta'];
$cdd = $fdesde . ' 00:00:00';
$cht = $fhasta . ' 23:59:59';
##$fdd = strtotime($cdd);
##$fht = strtotime($cht);
}


#Consulta  de registros PW
$sqlpw = "SELECT date(a.PWTSTP) as Fecha, time(a.PWTSTP) as Hora,a.PWUSRN as Usuario, a.PWJOB as Trabajo,a.PWNBR as Nro_Trabajo, a.PWPGM as Programa, a.PWPGMLIB as Biblioteca,trim(a.PWRADR) as Direccion_IP, a.PWTYPE as Tipo_de_Intento, b.PWTYPEDS as Detalle_del_Intento
        from MYLIB.QAUDITPW a left  join MYLIB.AUDPWTYPE b
        on a.PWTYPE = b.PWTYPE
        where a.PWTSTP >= '$cdd' and a.PWTSTP <= '$cht'
        order by a.PWTSTP" ;

##echo $sqlpw;
$result=odbc_exec($conn,$sqlpw);


echo '<table border=1 cellspacing=0 cellpadding=2 ><tr>';
    $fila = 1;
     while (odbc_fetch_row($result)) {
         $FieldQ = odbc_num_fields($result);

         if ($fila==1) {
             for ($i = 1; $i <= $FieldQ ; $i++)
             {
             $fn = odbc_field_name($result,$i);
             echo "<th bgcolor=#AFAFAF>$fn</th>";
             }
             echo "</tr>";
         }
         $fila++;
        echo "<tr>";
         $td = "<td bgcolor=#DCEBF9>";
         if (($fila/2)==intval($fila/2)) {
             $td = "<td bgcolor=#FFFFFF>";
         }

         for ($i = 1; $i <= $FieldQ ; $i++)
         {
         $fn = odbc_field_name($result,$i);
         $Campo = odbc_result($result,$fn);
         echo $td . $Campo . "</td>";
         }
         echo "</tr>";

    }
    echo "</table>";

$clo = odbc_close($conn);
?></b></i>

//---------------------------------------//



El programa Connection.php es común en ambos reportes




<?php
$user="usuario"; #a valid username that will connect to the DB
$pass="password"; #a password for the username
$dsn = "Driver={Client Access ODBC Driver (32-bit)};System=IP_SERVIDOR;BLOCKSIZE=256;BLOCKFETCH=0;
        DateFormat=6;DSP=0;COMPRESSION=1;PREFETCH=1;TRANSLATE=1;ExtendedDynamic=1;";
#Connect to the Database with ODBC
# $conn is defined and loaded using the odbc_connect PHP directive.
$conn=odbc_connect($dsn,$user,$pass);

#Check Connection
if ($conn == false) {
echo "<br>";
echo "Not able to connect to database<br>";
echo "<br>";
}
?>


//---------------------------------------//

Hay que preparar un par de tablas con descripciones, para que los reportes tengan algo de sentido

MYLIB.AUDPWTYPE

PWTYPE  PWTYPEDS                                                              
  A     APPC bind failure                                                     
  C     User authentication with the CHKPWD command failed                    
  D     Service tools user ID name not valid                                  
  E     Service tools user ID password not valid                              
  P     Password not valid                                                    
  Q     Attempted signon (user authentication) failed because user profile is d
  R     Attempted signon failed because password was expired. This audit record
  S     SQL Decryption password is not valid                                  
  U     User name not valid                                                   
  X     Service tools user ID is disabled                                     
  Y     Service tools user ID not valid                                       
  Z     Service tools user ID password not valid
                              
 


MYLIB.AUDAFTYPE

AFVIOL  AFVIOLDS                                                             
  A     Not authorized to object                                             
  B     Restricted instruction                                               
  C     Validation failure (see J5 offset 639)                               
  D     Validation failure (see J5 offset 639)                               
  E     Hardware storage protection error, program constant space violation  
  S     Default sign-on attempt                                              
  T     Not authorized to TCP/IP port                                        
  F     ICAPI authorization error                                            
  G     ICAPI authentication error                                           
  H     Scan exit program action (see J5 offset 639)                         
  I     System Java  inheritance not allowed                                 
  J     Submit job profile error                                             
  K     Special authority violation                                          
  N     Profile token not a regenerable token                                
  O     Optical Object Authority Failure                                     
  P     Profile swap error                                                   
  R     Hardware protection error                                             
  U     User permission request not valid                                     
  V     Profile token not valid for generating new profile token              
  W     Profile token not valid for swap                                      
  X     System violation                                                      
  Y     Not authorized to the current JUID field during a clear JUID operation
  Z     Not authorized to the current JUID field during a set JUID operation. 
 


Esto se probó corriendo en Windows con WAMPSERVER y System i Access V6R1 sin problema alguno.

19 dic 2014

IBM i (iSeries / AS/400) - Haciendo respaldos en la nube/red (Cloud Backup)

Hoy voy a volverme algo tecnócrata. No creo que sea necesario que empiece con una explicación sobre que es la nube, pero para quienes no sepan de que se trata, pueden consultar información en WikiPedia http://es.wikipedia.org/wiki/Cloud_computing .

Lo cierto es que hoy disponemos de espacio en múltiples servidores, algunos gratuitos, otros los estamos pagando.
Podemos mencionar los casos de Microsoft OneDrive (ex-SkyDrive), que al comprar Microsoft Office 365 por 1 año nos da espacio de 1 TB, Dropbox que nos "regala" 5 GB o nuestro hosting web, que nos da una cantidad de espacio que dificilmente llenemos con nuestros archivitos HTML.

También es cierto que a pesar de seguir usando medios magnéticos como cartuchos de cinta para hacer respaldos, estos requieren de ser migrados cada tantos años, pues la tecnología avanza y los dispositivos que permiten leer estos medios se vuelven obsoletos.

Y por último, y por qué no decirlo, la moda de hacer algo en "la nube". Hoy las empresas están explorando y haciendo uso de servicios como Amazon EC2, ES2, Google Drive y Google Storage, Servidores Dedicados (el que prefieran), Dropbox para empresas, OneDrive empresarial, Softlayer y su nube "bare-metal", etc.

Pero cómo hacemos para subir información a esos servicios?
En el caso de DropBox es relativamente fácil: Se dispone de una suerte de "USB Virtual" donde copiamos los archivos desde Windows, y alguna gente ha podido crear scripts usando cURL para subirlos desde Linux e incluso Unix. Con OneDrive, al ser de Microsoft, sólo se puede hacer desde Windows, y una gran desventaja a mi criterio, es que deja una copia "local" en el disco de la PC.

Pero volvamos al mundo de los servidores de las empresas. Cómo saco jugo a tanto espacio tan barato para almacenar mis respaldos que no siempre son indispensables para mi operación, como podrían ser los servidores de desarrollo y pruebas de la empresa. También podemos hacerlo sobre servidores productivos, pero a mi criterio sería bueno contar con un servicio de buen desempeño y con ciertas garantías como es el caso de SoftLayer.

Como siempre, este tipo de cosas requieren de algún tipo de "solución". En nuestro caso hablamos de diferentes componentes que se integran para poder hacer el respaldo:

1) Un lugar donde salvarlo (asumamos un servidor FTP en nuestro hosting, que es lo más simple)
2) Un medio de transporte/comunicación. Lo ideal es usar Internet por su costo, pero si los datos son confidenciales (como suele ser el caso), debemos pensar en mecanismos de seguridad en los datos, en el medio de comunicación, o en ambos.
3) Un mecanismo de respaldo. Hasta ahora usamos medios físicos. Para medios electrónicos necesitamos un software de respaldo que guarde a disco en un formato de archivo que podamos transferir, o bien algún tipo de sistema de archivado, como solía ser el WINZIP de Windows.
4) Un mecanismo de reducción/compresión de la información. Existen diferentes mecanismos para hacer esto, tanto a nivel servidor como en una PC. Los mejores mecanismos son los que usan deduplicación, pero requieren producos licenciados o el uso de algunas componentes "experimentales" como podrían poner en riezgo la integridad de los datos, adicionado a un poder de cómputo más elevado. En esta nota trataremos de mecanismos de compresión y de algunas técnicas de respaldo que permiten reducir el volumen de la información.
5) Un mecanismo de restauración. Si respaldamos debemos poder restaurar la información.

Si hablamos de sistemas Linux/*nix (AIX/HP-Ux/Solaris/etc.) la solución suele ser medianamente simple, tar+gzip. Generamos un archivo tar del "filesystem" o directorio deseado y lo "g-zipeamos" para reducir su tamaño. El gzip es, según diferentes análisis, el mecanismo más veloz a la hora de comprimir información, aunque quizá no sea el más eficiente en cuanto a tamaño de los archivos comprimidos.

Volviendo al mundo de las pantallas verdes, todo es bastante diferente cuando se trata del AS/400-iSeries-IBM i. Su sistema operativo tiene algunas restricciones en cuanto a nombres largos, compatibilidad con Linux/*nix y por lo tanto con programas de compresión.
La gran ventaja que tenemos es el poderoso comando SAVLIB y sus hermanos SAVOBJ y SAVCHGOBJ, así como las posibilidad de respaldar en cintas o en SAVE FILES (*SAVF)
Por otro lado, tenemos herramientas como el PASE o el QSHELL, que nos permiten ejecutar algunos comandos de AIX (el sabor IBM del Unix).

Juntando todo esto podemos crear un mecanismo de respaldo que haga lo siguiente:

1) Que diariamente haga un salvado de cada biblioteca que indicemos en un SAVF (archivo de respaldo)
2) Comprima la información con algún mecanismo de encrypción que nos permita asegurar la información
3) Transfiera los datos a nuestro sitio, en este caso FTP.

Las componentes de nuestra "solución" son las siguientes:

a) Un programa/script que llame a mis rutinas: BP7ZI (CLLE)

PGM                                                              
    SNDMSG     MSG('Creando archivo de comandos FTP') +          
                 TOUSR(*REQUESTER)                               
    CALL MYLIB/BKPFTP01b                                     
    SNDMSG     MSG('Salvando bibliotecas a disco') +             
                 TOUSR(*REQUESTER)                               
    CALL MYLIB/BKPSAVF01i                                    
    SNDMSG     MSG('Enviando respaldos por FTP') +               
                 TOUSR(*REQUESTER)                               
    CALL MYLIB/BKPFTP02b                                     
ENDPGM  
  
      
                     
                            


b) Un programa  que cree los comandos que ejecutaré en modo batch para transferir los archivos


BKPFTP01b (CLLE)                                

PGM                                                 
DCL        VAR(&SYSDATE) TYPE(*CHAR) LEN(6)         
DCL        VAR(&LILIAN) TYPE(*CHAR) LEN(4)          
DCL        VAR(&WDATE) TYPE(*CHAR) LEN(8)           
DCL        VAR(&JUNK1) TYPE(*CHAR) LEN(8)           
DCL        VAR(&JUNK2) TYPE(*CHAR) LEN(23)          
DCL        VAR(&FTPCMD) TYPE(*CHAR) LEN(256)        
DCLF       FILE(MYLIB/BKPLIB)                   
RTVSYSVAL  SYSVAL(QDATE) RTNVAR(&SYSDATE)           
CALLPRC    PRC(CEELOCT) PARM((&LILIAN) (&JUNK1) +   
             (&JUNK2) (*OMIT))                      
CHGVAR     VAR(%BIN(&LILIAN)) VALUE(%BIN(&LILIAN) ) 
CALLPRC    PRC(CEEDATE) PARM((&LILIAN) ('YYYYMMDD') +
             (&WDATE) (*OMIT))                      
QSH        CMD('echo open ftp.myserver.com.mx > +  
           /home/backupsav/backupsav.ftp')          
QSH        CMD('echo user myuser
mypassword +

           >>/home/backupsav/backupsav.ftp')
QSH        CMD('echo bin    >> +                   
           /home/backupsav/backupsav.ftp')         
QSH        CMD('echo namefmt 1 >> +                
           /home/backupsav/backupsav.ftp')         
CHGVAR     VAR(&FTPCMD) VALUE('echo mkdir ' *bcat +
             &WDATE *BCAT ' >> +                   
             /home/backupsav/backupsav.ftp')       
QSH        CMD(&ftpcmd )                                
CHGVAR     VAR(&FTPCMD) VALUE('echo cd ' *bcat &WDATE + 
             *BCAT ' >> /home/backupsav/backupsav.ftp') 
QSH        CMD(&ftpcmd )                                
CHGVAR     VAR(&FTPCMD) VALUE('echo put +               
             /home/backupsav/savsecdta.7z  +            
           >> /home/backupsav/backupsav.ftp')           
                                                        
QSH        CMD(&ftpcmd )                                
                                                        
LEE:        

RCVF                                                
MONMSG     MSGID(CPF0864) EXEC(GOTO CMDLBL(FIN))    
                                                                
CHGVAR     VAR(&FTPCMD) VALUE('echo put +           
                         /home/backupsav/'  *TCAT &ODOBNM *TCAT +
                         '.7z' *BCAT +                          
                       '>> /home/backupsav/backupsav.ftp')      
QSH        CMD(&FTPCMD)                             
GOTO       CMDLBL(LEE)                              
FIN:

QSH    CMD('echo quit >>/home/backupsav/backupsav.ftp')   
CPYFRMSTMF FROMSTMF('/home/backupsav/backupsav.ftp') +    
             TOMBR('/QSYS.LIB/MYLIB.LIB/FTPBKP.FILE/+ 
             FTPIN.MBR') MBROPT(*REPLACE)                 
ENDPGM


c) un archivo MYLIB/BKPLIB que contenga los nombres de las bibliotecas a respaldar. Este lo he creado usando DSPOBJD (Display Object Description) de la biblioteca QSYS para todos los objetos de tipo *LIB y luego depuré lo que no deseaba.

d) Un servidor de FTP: en este caso se llama ftp.myserver.com.mx con usuario "myuser" y password "mypassword" (son sólo ejemplos)

e) Un directorio en el IFS llamado /home/backupsav. Lo podemos crear con 
el siguiente comando en pantalla verde:
QSH CMD('mkdir /home/backupsav')     
f) Un programa para hacer el salvado
 BKPFTP01b (CLLE)

             PGM                                                   
             DCL        VAR(&ENCKEY) TYPE(*CHAR) LEN(64)           
             DCL        VAR(&zipCMD) TYPE(*CHAR) LEN(256)          
             dcl        var(&FECHA) type(*CHAR) LEN(8)             
             DCL        VAR(&DIA)  TYPE(*CHAR) LEN(2)              
             DCL        VAR(&NDIA) TYPE(*DEC) LEN(2 0)             
             DCL        VAR(&MES)  TYPE(*CHAR) LEN(2)              
             DCL        VAR(&ANIO)  TYPE(*CHAR) LEN(2)             
             DCL        VAR(&DOW) TYPE(*CHAR) LEN(4)               
             DCLF       FILE(MYLIB/BKPLIB)                     
             RTVDTAARA  DTAARA(MYLIB/BKP7ZPSWD *ALL) +         
                          RTNVAR(&ENCKEY)                          
             CRTLIB LIB(BACKUPSAV) TEXT('Biblioteca de Backup')    
             MONMSG     MSGID(CPF2111)                             
             CLRLIB     LIB(BACKUPSAV)                             
             /* Borro los archivos  SAVF y 7z que hayan quedado */ 
            QSH        CMD('/QOpenSys/usr/bin/sh -c " +          
                       rm /home/backupsav/*.7z"')                
            QSH        CMD('/QOpenSys/usr/bin/sh -c " +          
                       rm /home/backupsav/*.savf"')              
            /* Creo el archivo  SAVF con perfiles de usuarios */ 
            CRTSAVF    FILE(BACKUPSAV/SAVSECDTA) TEXT('Seguridad')
            SAVSECDTA  DEV(*SAVF) SAVF(BACKUPSAV/SAVSECDTA) +    
                         DTACPR(*NO) COMPACT(*NO)                
            /* Muevo el archivo  SAVF al IFS */                  
            QSH        CMD('/QOpenSys/usr/bin/sh -c " +              
                         mv /QSYS.LIB/BACKUPSAV.LIB/SAVSECDTA.FILE + 
                         /home/backupsav/SAVSECDTA.savf"')           
            /* Comprimo el archivo  SAVF */                          
            CHGVAR     VAR(&zipCMD) VALUE('/QOpenSys/usr/bin/sh -c " +
                        7za a -p' *tcat +                            
                        &ENCKEY *BCAT '/home/backupsav/savsecdta.7z +
                          /home/backupsav/savsecdta.savf"')          
                                                                     
            QSH        CMD(&ZIPCMD)                               
            /* Borro el archivo  SAVF */                          
            QSH        CMD('/QOpenSys/usr/bin/sh -c " +           
                       rm /home/backupsav/SAVSECDTA.savf"')       
            MONMSG     MSGID(CPF0000)                             
LEE:        RCVF                                                  
            MONMSG     MSGID(CPF0864) EXEC(GOTO CMDLBL(FIN))      
            CRTSAVF    FILE(BACKUPSAV/&ODOBNM) TEXT(&ODOBTX)      
            MONMSG     MSGID(CPF0000)                             
            RTVSYSVAL  SYSVAL(QDAYOFWEEK) RTNVAR(&DOW)               
            RTVSYSVAL  SYSVAL(QDAY) RTNVAR(&DIA)                     
            RTVSYSVAL  SYSVAL(QMONTH) RTNVAR(&MES)                   
            RTVSYSVAL  SYSVAL(QYEAR) RTNVAR(&ANIO)                   
            CHGVAR     VAR(&FECHA) VALUE('01' *TCAT &MES *TCAT +     
                         '20' *TCAT &ANIO)                           
            CHGVAR     VAR(&NDIA) VALUE(&DIA)                        
            IF         COND((&DOW  = '*MON') *OR (&NDIA=1)) THEN( +  
                       SAVLIB LIB(&ODOBNM) +                         
                         DEV(*SAVF) SAVF(BACKUPSAV/&ODOBNM) +        
                         SAVACT(*LIB) ACCPTH(*YES) DTACPR(*NO) +     
                         COMPACT(*NO) )                              
            ELSE   CMD(   +                                          
                SAVCHGOBJ  OBJ(*ALL) LIB(&ODOBNM) DEV(*SAVF) +       
                         REFDATE(&FECHA) REFTIME(000000) +           
                         SAVF(BACKUPSAV/&ODOBNM) SAVACT(*LIB) +      
                         ACCPTH(*YES) DTACPR(*NO) COMPACT(*NO))      
            /* MUEVO el archivo  SAVF al IFS */                       
            CHGVAR     VAR(&ZIPCMD) VALUE('/QOpenSys/usr/bin/sh -c " +
                         mv /QSYS.LIB/BACKUPSAV.LIB/' *TCAT &ODOBNM + 
                         *TCAT '.FILE /home/backupsav/' *TCAT +       
                         &ODOBNM *TCAT '.savf"')                      
                                                                      
            QSH        CMD(&zipCMD)                                   
            /* Comprimo con 7zip y clave el archivo  SAVF */          
            CHGVAR     VAR(&ZIPCMD) VALUE('/QOpenSys/usr/bin/sh -c " +
                       7za a -p' *tcat +                              
                       &ENCKEY *BCAT '/home/backupsav/' *TCAT +       
                       &ODOBNM *TCAT '.7z /home/backupsav/' *TCAT +   
                       &ODOBNM *TCAT '.savf"')                        
                                                                      
            QSH        CMD(&zipCMD)                                   
            /* Borro el archivo  SAVF */                              
            CHGVAR     VAR(&ZIPCMD) VALUE('/QOpenSys/usr/bin/sh -c " +
                         rm /home/backupsav/' +        
                        *tcat &ODOBNM *TCAT '.savf"') 
             QSH        CMD(&zipCMD)                  
                                                      
                                                      
             GOTO       CMDLBL(LEE)                   
 FIN:        ENDPGM                                   


g) Un programa que envíe los archivos a nuestro sitio FTP

BKPFTP02b (CLLE)

pgm                                                                    
           OVRDBF     FILE(INPUT) TOFILE(MYLIB/FTPBKP) MBR(FTPIN)  
           CLRPFM     FILE(MYLIB/FTPBKP) MBR(FTPOUT)               
           OVRDBF     FILE(OUTPUT) TOFILE(MYLIB/FTPBKP) +          
                        MBR(FTPOUT)                                    
           FTP RMTSYS(NOTFOUND)                                        
           DLTOVR FILE(*ALL)                                           
endpgm                                                                 


h)  Una Data Area (*DTAARA) donde guardaremos la llave de encripción
 MYLIB/BKP7ZPSWD

i) Un archivo donde pondremos la información del FTP de entrada y el log de salida:

Un archivo MYLIB/FTPBKP que contenga un miembro llamado FTPIN y otro FTPOUT

j) El programa 7-ZIP compilado para IBM i. Lo podemos encontrar junto con las instrucciones en la página http://www.scottklement.com/p7zip/

k) Obviamente necesitaremos el QSHELL y el PASE instalados, así como IBM i V5R4 en adelante (posiblemente funcione en versiones anteriores, pero no puedo garantizarlo)

l) Espacio para alojar nuestra biblioteca más grande x 2, y un 15% de espacio del total del disco libre.

m) Una entrada en JOBSCDE (podemos usar WRKJOBSCDE) para que ejecute de lunes a viernes (al menos así lo tengo yo)

Lo que hace este programa es ejecutarse todos los días a una hora determinada, y si el día es el 1ero de cada mes o lunes ejecuta un respaldo "completo" de las bibliotecas indicadas en el archivo BKPLIB. Si es OTRO día, simplemente ejecutará un respaldo incremental desde el día 1ero del mes hasta la fecha, para reducir el espacio.
El programa ejecutará un SAVLIB en los respaldo completos por cada biblioteca, luego la comprimirá con 7-ZIP y encriptará usando AES256 con la llave que guardemos en la DTAARA BKP7ZPSWD para que podamos dormir medianamente tranquilos.
Una vez comprimido borrará el archivo SAVE FILE del disco, para liberar espacio y continuará con la biblioteca siguiente.
Los días que NO son 1ero de mes ni lunes hará un salvado incremental desde el 1ero a la fecha usando SAVCHGOBJ, y lo comprimirá con el mismo mecanismo. Esto reduce el tamaño notablemente (en mi caso a un 30% en el peor de los casos).

Una vez terminado, el respaldo se transfiere a un sitio FTP (estoy trabajando en un mecanismo que use SCP, mucho más seguro, pero no funciona con un hosting web común).

Obviamente que es un proceso perfectible, pues se puede crear un mecanismo eficiente para editar las bibliotecas, agregar filtros sobre los archivos, crear una clave diferente por cada biblioteca, agregar algún tipo de GUI para revisar los logs de respaldo y transferencia, etc.

La idea era brindarles un mecanismo aceptable que permita subir información segura a un sitio FTP. Las mejoras se las dejo a Uds.

Cómo conectarme a un servidor remoto en una red protegida - Versión actualizada

En un artículo anterior describí cómo conectarse a un equipo remoto en una red protegida http://diego-k.blogspot.mx/2014/12/como-conectarme...