Administrar proyectos libres con Launchpad y Bazaar
En los últimos días estuve ocupado creando una herramienta que me permitiera realizar consultas sobre un servicio LDAP para obtener información de los usuarios de un dominio. La herramienta en cuestión la realicé en Python y me pareció que era lo suficientemente interesante como para compartirla con la comunidad libre para que cualquiera pueda utilizarla y/o modificarla.
Esto me llevó a la búsqueda de un servicio de versionado y hosting libre donde subir el proyecto. Algunos años atrás hubiera utilizado SourceForge sin pensarlo, pero gracias a su cambio de política hacia una censurista y asquerosamente repudiable (que raro que provenga de una ley yankee "defensores de la libertad"...) que deja afuera a los países Iran, Corea del Norte, Cuba, Siria, Libia y Sudan, decidí buscar una alternativa que sea realmente libre. La idea es compartir código entre comunidades sin importar estatus social, país de orígen, color de piel, etc, etc, y SourceForge no cumple con este importantísimo requisito. Lo mismo sucede con Google Code, lo cual elimina al segundo gran proveedor de software open source.
Por ello, utilizando la muy completa entrada de wikipedia Comparison of open source software hosting facilities, revisé cuál de los servicios se adaptaba mejor a lo que quería. Por orden de popularidad tenemos SourceForge (descartado), Launchpad, Google Code (descartado), GitHub y Assembla. De entre estos elegí Launchpad porque los términos de servicio de GitHub y Assembla no me terminaban de agradar. Increíblemente GitHub impone copyright sobre el código html/javascript/css de SU propia página... un site que promulga el hosting de aplicaciones open source y no es libre, no me agrada. Además, los mencionados servicios están hosteados en USA y aplican sus leyes, con lo cual corremos el riesgo que suceda lo mismo que con SourceForge.
Launchpad es de Canonical Ltd. (la gente detrás de Ubuntu) y esto también me genera dudas, dado que desconfío de las intenciones de dicha compañía... pero no encontré nada malo en sus términos de servicio (es más, no encontré términos de servicio para hosting de proyectos...), es una comunidad muy grande con proyectos importantes (MySQL, Ubuntu, Enlightenment, Bazaar, entre otros), no está hosteado en USA (por lo que vi, los servidores son de Gran Bretaña) y el administrador de proyectos me parece bueno.

A continuación describiré los pasos necesarios desde la creación hasta la publicación de una versión descargable de nuestro proyecto, utilizando Launchpad y Bazaar.


Creación del proyecto en Launchpad

Para crear un proyecto en Launchpad, primero debemos registrar una cuenta, si es que no poseemos una. La registración es simple y no requiere muchos datos.
Una vez que estamos registrados y logueados, nos dirigimos a https://launchpad.net/projects/+new para registrar nuestro proyecto.
Para tener una idea sobre cómo se administran los proyectos en Launchpad, les recomiendo leer la ayuda oficial.


Creación del proyecto en Bazaar

Launchpad utiliza Bazaar para el versionado de proyectos, por lo que necesitaremos conocer una lista de comandos básicos de este sistema. Es posible importar branchs de otros manejadores de versionado como Git, Mercurial, Subversion y CVS, pero si recién están comenzando, o si nunca utilizaron un manejador de versiones, por qué no probar con Bazaar?

Instalar Bazaar en debian y derivados es tan simple como ejecutar:
# apt-get install bzr
Una vez instalado, configuramos nuestro nombre de usuario:
$ bzr whoami "demasiadovivo <demasiadovivo@spameame.com>"
y verificamos que el nombre se registró correctamente:
$ bzr whoami
demasiadovivo <demasiadovivo@spameame.com>
Luego del simple setup inicial, nos metemos con la creación y administración de proyectos utilizando este sistema de versionado.
El primer paso es inicializar el proyecto:
- dirigirse al directorio donde se encuentra el código del proyecto:
$ cd /path/mi-proyecto
- inicializar:
$ bzr init
Al hacer esto, Bazaar crea el primer branch y almacena los datos en un directorio denominado ".bzr". Un branch es un registro de todos los commits que se han hecho. Pueden existir varios branches en paralelo, los cuales luego se pueden combinar (merge).
Para agregar los archivos que deseamos seguir, se utiliza el comando bzr add . Para agregar todos los archivos del directorio del proyecto, podemos simplemente ejecutar:
$ bzr add
adding README.txt
adding useraudit.cfg
adding useraudit.py
adding useraudit_cli.py
Ahora, cuando tenemos código sobre el cual deseamos crear un snapshot, realizamos un commit. En el commit es posible agregar un comentario, indicando la razón del mismo:
$ bzr commit -m "Primer commit"
Committing to: /ruta/mi-proyecto/
added README.txt
added useraudit.cfg
added useraudit.py
added useraudit_cli.py
Committed revision 1.
Podemos ver el historial de commits con:
$ bzr log
------------------------------------------------------------
revno: 1
committer: demasiadovivo

branch nick: mi-proyecto
timestamp: Sun 2011-06-05 16:20:03 -0300
message:
Primer commit

Subir el código a Launchpad

Para poder subir código a Launchpad, primero deberán generar un par de claves SSH, para lo cual pueden seguir el artículo Automatización de transferencias por SFTP, y a continuación publicar la clave pública en https://launchpad.net/~<usuario>/+editsshkeys
Recuerden que la clave pública se encuentra en /home/<usuario>/.ssh/id_rsa.pub, si es que no eligieron otro path al generar el par de claves.
Con esta clave podremos autenticarnos ante Launchpad y así subir código.

Una vez que registramos nuestra clave SSH, podemos subir el commit a Launchpad ejecutando:
$ bzr push sftp://<usuario>@bazaar.launchpad.net/~<usuario>/<mi-proyecto>/<branch>
donde:
bazaar.launchpad.net es la dirección de Bazaar en Launchpad
<usuario> es nuestro usuario en Launchpad
<mi-proyecto> es el proyecto que registramos en Launchpad
<branch> es el branch que deseamos crear
Si queremos hacer un commit a un branch que ya existe, deberemos ejecutar el mismo comando pero con un parámetro adicional:
$ bzr push --use-existing sftp://@bazaar.launchpad.net/~<usuario>/<mi-proyecto>/<branch>
Luego de crear el branch, es posible (al menos a mi me sucedió), que Launchpad nos diga que el proyecto todavía no tiene un branch. Esto es que Launchpad no sabe cuál es el branch principal, por lo cual nos dirigimos a https://launchpad.net//trunk/+setbranch y elegimos la opción "Link to a Bazaar branch already on Launchpad", y en el textbox donde indicamos el branch ponemos la dirección del branch recién creado:
~<usuario>/<mi-proyecto>/<branch>

Generar un release

Una vez que estamos lo suficientemente conformes con nuestro código y queremos que usuarios finales descarguen el programa, debemos generar un release y para generar un release, necesitamos un milestone.

Los milestones son puntos específicos en la vida de una serie (series en inglés), y una "serie" representa la serie de lanzamientos de una versión mayor. Los puntos representados por un milestone pueden ser beta tests, release candidates, o puntos menor y mayor de lanzamiento. Pueden leer más sobre series, milestones y releases en Projects/SeriesMilestonesReleases.
En fin, para crear un milestone, nos dirigimos a la sección "Milestones and releases" de la página principal del proyecto y le damos al link "Create milestone" (https://launchpad.net//trunk/+addrelease).
Entre la información que debemos proveer, el único campo obligatorio es el nombre, el cual se toma como la versión.

Una vez que tenemos el milestone creado, podemos proceder a realizar el release. Hacerlo es tan simple como darle al link "Create release" que se encuentra en el milestone bajo el título "Expected" y setear una fecha.

Cuando terminamos de crear el release, el site nos regresa al milestone, donde podemos ver que la fecha del release se actualizó. Ahora sí podemos agregar un archivo tarball descargable que contenga el programa completo.
Pueden generar el tarball de diferentes formas, pero si lo van a hacer desde la consola, deberán dirigirse al directorio padre del proyecto y ejecutar lo siguiente:
$ tar -czvf mi-proyecto_v0.1.tar.gz --exclude-vcs mi-proyecto/
donde la opción --exclude-vcs evita que tar incluya el directorio que utiliza Bazaar para sus datos (es decir, el ".bzr").
Ya tenemos todo lo necesario para subir el tarball con el programa completo y que se pueda descargar. Para ello, en el milestone seleccionamos "Add download file". En la interfaz de upload debemos proveer tanto el archivo como una descripción de lo que contiene el mismo (por ejemplo "mi-proyecto 0.1 tarball"). También es posible proveer una firma GPG, lo cual brinda mayor seguridad al cliente que descarga, ya que puede confiar que el archivo ha sido creado por nosotros. Para poder firmar el archivo debemos poseer una un par de claves GPG (o PGP), y la clave pública debe estar publicada en algún lugar de confianza.
El mismo site da la ayuda de cómo crear la firma digital del archivo:
gpg --armor --sign --detach-sig
lo cual crea el archivo filename.asc que después debemos subir utilizando el campo "GPG signature".


Trabajo Terminado

Si quieren ver el resultado final, pueden ingresar al site de mi proyecto. También están invitados a descargar, testear, recomendar, compartir, etc, etc, el programa. Ya escribiré algún post sobre la herramienta =)


Referencias

- Launchpad Projects
- Bazaar in five minutes
- Hosting your code on launchpad and bazaar
Estadísticas sobre uso de recursos en GNU/Linux
Si son sysadmins, necesitarán alguna forma de ver cómo se utilizan los recursos de nuestros sistemas, sobre todo cuando las cosas andan mal. Ver estadísticas sobre el uso de recursos permite tomar varias decisiones, como comprar hardware, separar servicios, virtualizar, o para solucionar errores en caso de procesos glotones, entre otras cosas.
Si bien en GNU/Linux todas las estadísticas se almacenan en el /proc, es necesario contar con alguna herramienta que las interprete y las haga más amigables. Para ello, contamos con sysstat, un paquete que provee varias herramientas para monitorear la performance del sistema y el uso de recursos.
Los componentes más interesantes de sysstat son:
- sar: reúne, reporta y almacena información de actividad del sistema (CPU, memoria, discos, interrupciones, interfaces de red, TTY, tablas del kernel, etc). Esta herramienta provee muchas opciones para filtrar los datos que deseamos obtener, y permite almacenar los resultados en archivos binarios que luego se pueden visualizar con el mismo sar. Es posible indicar la cantidad de muestras a tomar y el intervalo de tiempo entre muestras.

- sadc (system activity data collector): se encarga de recoger información del sistema una dada cantidad de veces, a intervalos específicos de tiempo, y se utiliza como backend de sar. Escribe en formato binario en un dado archivo o en la standar output. Si no se especifica la cantidad de muestras a tomar, éste escribe infinitamente.

- sa1: un shell script que utiliza sadc para generar el archivo binario /var/log/sa/sadd o /var/log/sysstat/sadd (donde dd es el día del mes, por ejemplo sa16 indica el día 16). Este script está designado para utilizarse con cron (ver /etc/cron.d/sysstat).

- sa2: otro shell script que utiliza sadc para generar reportes diarios en el archivo binario /var/log/sa/sardd o /var/log/sysstat/sardd (donde dd es el día del mes). Este script también está designado para utilizarse con cron (ver /etc/cron.daily/sysstat).

- sadf: permite mostrar el contenido de archivos binarios sar en diferentes formatos (CSV, XML, etc). El formato de salida default es uno fácilmente parseable por diferentes herramientas shell como grep, awk, sed, etc.

- pidstat: reporta estadísticas de procesos como uso de CPU, memoria, I/O, etc. Como en sar, es posible indicar la cantidad de muestras y el intervalo de tiempo entre muestras.
Si deseamos medir la utilización de recursos en el server, lo mejor es dejar que sa1 y sa2 generen los correspondientes logs durante un día, una semana, un mes o el tiempo que quieran, y luego visualizarlos con sar. Por defecto las muestras se toman cada 10 minutos, lo cual provee un buena visión sobre como se utilizan los recursos.
Otra posibilidad es ejecutar sar con el parámetro "-o archivo" para almacenar los datos en un archivo binario, e indicarle el intervalo de segundos entre muestras y la cantidad de muestras a tomar. Por ejemplo, el siguiente comando almacena todos los datos de utilización de recursos en el archivo "revision" cada 5 minutos:
sar -A -o revision 300
Para poder parsear el archivo recién generado con sar, es necesario utilizar el parámetro -f, por ejemplo:
sar -A -f revision
En la mayoría de los casos lo que necesitamos visualizar es uso de memoria, CPU, disco, swap y red. Todo esto se puede hacer con el siguiente comando:
sar -rudpW -n DEV,SOCK
donde:
-r muestra uso de memoria
-u muestra uso de CPU
-d muestra uso de disco
-p imprime el nombre de los dispositivos de forma agradable (usado con -d)
-W muestra estadísticas de swap
-n DEV,SOCK muestra estadísticas de las interfaces de red y cantidad de sockets utilizados.

Si no se utiliza el parámetro -f, sar toma los datos de los logs generados por sa1 y sa2. En caso de utilizar un archivo binario generado con sar (por ejemplo revision), pueden usar el siguiente comando:
sar -rudpW -n DEV,SOCK -f revision
Como podrán observar, la salida de sar no es muy amigable para ser filtrada y/o graficada y/o almacenada en una base de datos, así que lo mejor es utilizar sadf. sadf provee un set de opciones propias y permite utilizar las opciones de sar luego de dos giones (--). Entre las opciones propias de sadf, son interesantes:
-d imprime la salida con los campos separados con punto y coma (;)
-D igual a -d pero utiliza timestamp en lugar de fechas human friendly
-h para imprimir los resultados en una sola línea
-p salida default, amigable para los parsers
-x imprime la salida en formato XML
El comando del ejemplo anterior, utilizando sadf para ser parseado fácilmente, quedaría:
sadf -- -rudpW -n DEV,SOCK
o con -h para separar fácilmente los campos en una planilla (Calc, Excel, etc)
sadf -h -- -rudpW -n DEV,SOCK
Además de las estadísticas anteriores, para hilar más fino a la hora de ver quién gasta los recursos, es necesario tener estadísticas de los procesos que se están ejecutando. Para ello contamos con pidstat, cuyos datos arrojados son similares a los de sar, pero por proceso. pidstat acepta varios argumentos para mostrar distintas estadísticas, pero para una vista básica, me alcanza la siguiente configuración:
pidstat -ru -h 300 | sed '1,2d'
donde:
-r muestra uso de memoria
-u muestra uso de CPU
-h muestra las estadísticas horizontalmente (todo en una sola tabla).
300 es el intervalo de tiempo (en segundos) para tomar muestras
sed '1,2d' elimina las primeras dos líneas de la salida (son un comentario y una línea en blanco)
pidstat no ofrece la opción de crear un archivo binario para almacenar los datos, por lo cual debemos redirigir la salida a un archivo para poder filtrarla luego.


Si lo que quieren son gráficos de utilización (siempre algo visual ayuda a captar mejor los datos), podemos utilizar la salida de sadf y graficar con Calc (o Excel), con gnuplot, o con la interesante Sysstat Graph. No tuve la oportunidad de probar esta última, pero aparenta ser muy útil. Está escrita en PHP, por lo cual es fácil de instalar.


Referencias

- Sysstat man
- How do I Find Out Linux CPU Utilization?
Configuración inicial de un PacketShaper
Un nuevo reto en redes se me presenta y felizmente afronto. Esta vez toca configurar un dispositivo denominado BlueCoat PacketShaper que permite medir la performance de aplicaciones que utilizan la red, categorizar y administrar el tráfico Web basándose en el contenido, garantizar calidad de servicio (QoS) y contener el impacto del tráfico indeseable. Realmente no había sentido nombrar este dispositivo, así que me sentía ansioso por ponerle las manos encima.
Como no hay mucha información en español (bah, en inglés tampoco...), me parece interesante comentarles cómo configurar uno de estos "bichos", por si les toca hacerlo en algún momento.

El dispositivo presenta tanto una interfaz Web como una por línea de comandos (CLI). Como es costumbre en este tipo de dispositivos, presenta un port serie a través del cual es posible accederlo para la configuración inicial.
En este artículo describiré cómo realizar la configuración inicial, necesaria para comenzar a analizar tráfico y crear reglas. Más adelante espero escribir al menos un artículo (espero sean más) sobre la creación de reglas para priorizar y analizar tráfico.

Como ya les comenté en los artículos sobre configuración de switchs y APs, necesitarán algún programa como minicom que se comunique a través del port serie, con una configuración 9600 bps - 8bits - sin paridad - 1 stop bit - sin control de flujo. Si están perdidos en este punto, lean el artículo Acceder dispositivos Cisco desde GNU/Linux con minicom.

Al revisar el aparato nos encontramos básicamente con 3 ports RJ45, uno denominado INSIDE, otro OUTSIDE y el último MGMT (Management). Dependiendo la estructura de nustra red y de lo que deseemos hacer con el PacketShaper, variará la forma en que conecten la entrada y la salida, pero en general se aplica la regla "placa INSIDE conectada a nuestra red y OUTSIDE al router/firewall que sale a la WAN (posiblemente Internet)". Esto es, si deseamos administrar el tráfico de internet, colocamos el PacketShaper entre nuestra red e internet de la siguiente forma:
RED Interna <----> INSIDE
OUTSIDE <----> firewall <----> router <----> Internet
La placa MGMT la utilizaremos para configurar el dispositivo una vez que le hayamos seteado una IP. Es conveniente que conectemos esta placa a una red segura porque es la puerta de entrada al dispositivo.
El dispositivo es "transparente" para nuestra red, no se necesita configurar IPs para las placas INSIDE y OUTSIDE. Es como si pincháramos el cable de red que conecta la red interna con el router y viéramos el tráfico que pasa. Incluso si el PacketShaper está apagado, la red funciona normalmente dado que se puentean las interfaces INSIDE y OUTSIDE.

Ahora si, nos conectamos al dispositivo a través de la conexión serie y abrimos minicom, gtkterm, cutecom, o HyperTerminal (si son Windowseros), y comenzamos la configuración.
Por suerte el software para realizar la configuración inicial es bastante amigable y nos va guiando a través de diferentes preguntas con breves explicaciones para que podamos entender qué estamos haciendo. La configuración que les propondré es para utilizar un PacketShaper sin PolicyCenter en una red Gigabit, aunque es lo suficiente genérica para poder utilizarla en otras configuraciones. La versión de PacketWise (soft del PacketShaper) que estoy utilizando es de la rama 8.4. A modo de itemizado, los primeros pasos de la configuración son:
- Elegir el modo local (standalone) porque vamos a configurar el PacketShaper de forma independiente, y no a través del software PolicyCenter (modo shared). Esto, claro está, cambiará si poseen PolicyCenter. En mi caso sólo poseo un PacketShaper y no es necesario.
- Setear una dirección IP (ej: 192.168.1.250), para poder acceder el dispositivo remotamente a través de la interfaz Web o ssh.
- Configurar la velocidad de las interfaces INSIDE y OUTSIDE (10bt, 100bt, auto) en auto, para que negocie la velocidad (aparentemente la única forma de funcionar en Gigabit es eligiendo auto).
- Setear el gateway de la red donde se encuentra conectado el dispositivo.
- Si se desea, en el siguiente paso es posible (y muy recomendable) configurar un servidor de DNS para resolver las direcciones.
- Tambiés es posible configurar el nombre de dominio, para ser agregado en lookups de nombres non-fully-qualified.

Passwords:
Luego de la configuración básica, el PacketShaper nos permite configurar dos passwords. Uno para "mirar" (look) el estado, reglas, reportes, etc, del PacketShaper, y otro para "tocar" (touch) que además de "mirar" permite configurar el aparato. Como siempre, es conveniente configurar ambos passwords.
En este punto encontré que (increíblemente) el sistema limita el largo del password a 19 caracteres. Todavía no puedo creer como sigue existiendo software que limite el largo de los passwords...

Data rates:
A continuación el software nos permite configurar las velocidades de entrada y salida, ya sea mediante números en bytes o a través de los valores simbólicos "T1","E1","10BT","Ethernet". Si elegimos "Ethernet", PacketShaper descubrirá cuál es la velocidad del link. En cualquier caso, luego el dispositivo nos recomendará valores, los cuales podemos aceptar o cambiar. Creo que lo mejor es utilizar "Ethernet" y a continuación acomodar los valores. Estos valores serán el Inbound y Outbound rate y son los que utilizaremos como ancho de banda máximo "garantizado". En general (dependiendo donde ubiquen el PacketShaper), estos valores son los de nuestra conexión a internet. Si poseemos una conexión de 3Mbps, lo máximo que entrará son 3Mbps, esto es el Inbound o datos entrantes desde internet a nuestra red. El caso del Outbound es el ancho de banda que tenemos de upload. Si poseemos una línea simétrica, el Outbound será igual al Inbound, es decir, 3Mbps.

Packet Shaping:
En el siguiente paso, es posible configurar si deseamos que el dispositivo aplique políticas de clasificación de tráfico o si simplemente deseamos ver el tráfico pasar (útil para monitorear). En mi caso, deseamos aplicar políticas, por lo cual utilizamos la opción "on".
También es posible que PacketShaper descubra aplicaciones y arme un árbol con clases basándose en en el tráfico observado. Esto es muy útil cuando no estamos del todo seguros cuál es el tráfico que existe, cuál filtrar y cuál priorizar. Mi recomendación es activar esta opción y luego agrupar/mover/eliminar las clases de acorde a la configuración que deseemos.

Configuración del horario:
El último paso de esta configuración inicial es configurar la zona horaria y la hora del dispositivo. Para la zona horaria existen strings que las identifican, los cuales pueden observar tecleando "?". En mi caso, la zona horaria es BuenosAires.
Para configurar la hora, debemos colocar un string con el formato yyyymmddhhmmss (es decir, el año con cuatro dígitos, seguido del mes, el día, la hora, y los segundos con sólo dos).


Con esto finalizamos la configuración inicial, y aceptamos salvarla en el almacenamiento. Ahora podrán acceder al dispositivo a través de la interfaz Web o ssh con la IP que configuraron anteriormente, utilizando la conexión de red al port denominado "MGMT".
Como instalar impresoras HP en Slackware 13.1

En este artículo voy a explicar el procedimiento, extremadamente sencillo, para instalar una impresora HP en un sistema Linux. A pesar de que el título indica que utilicé Slackware 13.1, el tutorial sirve para cualquier otra distribución.

La impresora que utilicé es una multifunción HP Deskjet F4480. Para las impresoras HP se dispone de los drivers libres HPLIP (HP Linux Imaging and Printing). En la mayoría de las distribuciones (como en Slackware 13.1) estos driver vienen incluidos, por lo que no es necesario instalar nada.

Para comenzar se debe conectar la impresora al puerto USB y encenderla. Luego se debe verificar que el servicio cupsd esté en ejecución (es necesario para crear al menos una cola de impresión). Para comprobarlo se puede ejecutar:
bash-4.1$ ps -lA | grep cupsd
4 S 0 2044 1 0 80 0 - 1452 epoll_ ? 00:00:00 cupsd

Si el servicio cupsd no se encuentra en ejecución, es necesario activarlo. En Slackware 13.1 se debe otorgar permiso de ejecución al script /etc/rc.d/rc.cups para que CUPS se ejecute automáticamente al iniciar el sistema. Para permitir la ejecución de CUPS se utiliza el comando:
bash-4.1# chmod +x /etc/rc.d/rc.cups

Y luego iniciamos cupsd:
bash-4.1# /etc/rc.d/rc.cups start
cups: started scheduler. [ OK ]

Teniendo CUPS en ejecución se procede a instalar el driver HPLIP correspondiente a la impresora, para esto se utiliza:
bash-4.1# hp-setup

Esto inicia un instalador gráfico muy amigable que permite instalar el driver de la impresora en tres simples pasos:

1) Seleccionar el tipo de conexión:


2) Seleccionar el dispositivo detectado:


3) Configurar el dispositivo:


Luego, desde KDE, seleccionamos K > Applications > System > Printing para editar las propiedades de la impresora.


La siguiente vez que iniciemos el sistema, veremos el ícono de HPLIP en la barra de notificaciones:



Feliz día del trabajador!
Compilar fuentes Java online
Pregunta: ¿Qué pasa si necesitamos compilar un fuente Java y no disponemos de un JDK en la workstation donde estamos trabajando?
Respuesta: Buscá en Google.

"En Internet está todo"

Esa fue la frase que solté cuando encontré esta herramienta muy útil, que nos puede sacar de un apuro. Leyendo un libro sobre programación en Java me surgieron dudas y necesité probar un ejemplo rápido. El problema era que no disponía de javac en la máquina donde estaba trabajando, y no tenía ganas de descargar e instalar el JDK.
Buscando en Google encontré este servicio muy útil que nos permite compilar fuentes java: JXXX Compiler Service.


El mismo permite subir archivos fuente y librerías y compilar el código. Si hubo errores durante el proceso de compilación, el servicio devuelve la salida de javac. Lo más interesante que tiene, es que si estamos compilando un applet lo incluirá directamente en la página para probar su funcionamiento.
Aunque el servicio es realmente útil, no lo utilizaría para compilar código propietario ya que estamos enviando nuestro código a terceros y no sabemos que harán con él (además de compilarlo). Pero nos sirve perfectamente para compilar código de prueba y sacarnos de un apuro.

Veamos un ejemplo utilizando el siguiente código:
class A {
}

class B extends A {
}

class C {
}

class test {
public static void main(String[] args) {
A a = new A();
B b = new B();
if (b instanceof A) {
System.out.println("b is an A");
}
else {
System.out.println("b is NOT an A");
}
}
}

Si compilamos el código anterior introduciendo un error, obtenemos lo siguiente:


Luego, corregimos el error y podemos descargar el código fuente Java compilado .class:


Ahora podemos descargar y ejecutar el .class:



Espero que les sirva!
Cómo descifrar un hash MD5 online
Aloe Vera
Este fin de semana me encontré con la necesidad de descifrar un password hasheado con md5. Para ello estuve a punto de utilizar el viejo y querido John the Ripper, pero un segundo antes se me ocurrió buscar alguna herramienta online que haga el trabajo por mí. Motivado por la vagancia, pero también para hacerlo más rápidamente (aprovechando la nube o rainbow tables), encontré buscando en Google este muy buen artículo: How to crack MD5 passwords online, el cual contiene una lista de servicios para reconstruir texto digerido mediante md5.
La mayoría de estos sitios utilizan rainbow tables (grandes tablas de hashes precomputados) con el objetivo de intercambiar tiempo de ejecución por memoria. De esta forma el resultado logrado es muy rápido (si es exitoso). Aunque también algunos utilizan diccionarios.

A continuación dejo la lista de servicios que realizan esta tarea. Les recomiendo que lean el artículo original, ya que el autor incluye una valuación de los mismos (calculada como la cantidad de hashes encontrados de un conjunto de 10):
www.tmto.org
md5.noisette.ch
md5decryption.com
www.c0llision.net
www.netmd5crack.com
www.md5decrypter.com
md5hashcracker.appspot.com
www.hashhack.com
isc.sans.edu
www.md5crack.com
passcracking.com
authsecu.com
md5.rednoize.com
md5.web-max.ca
www.cmd5.com
md5.thekaine.de
www.shell-storm.org
www.md5this.com
www.hashchecker.com
hashcrack.com
md5pass.com
md5pass.info

Cabe destacar que algunos de estos sitios incluyen una herramienta para generar hashes md5. No es recomendable utilizarlas para calcular hashes de nuestras contraseñas, ya que no sabemos si guardan los hashes que calculamos en sus bases de datos, lo cual las vuelve 100% crackeables ;)

Para lograr mi objetivo, tomé el primer servicio de la lista y obtuve el texto hasheado (admin1234) en 3.55 segundos. Lo que más me molestó fue que había probado sin éxito varias contraseñas, entre las que se encontraban 'admin', '1234' y 'admin123' pero no 'admin1234'!



Para finalizar, dejo un excelente artículo que explica cómo utilizar Google como password cracker (a esta altura creo que a Google le descubren más propiedades que al Aloe Vera). La justificación de esta técnica es que a veces los programadores incluyen hashes md5 en la URL (para ver la explicación detallada les recomiendo lean el artículo), lo que transforma a las bases de datos de Google en rainbow tables.


No me deja de sorprender la cantidad de tareas útiles que se pueden realizar utilizando Google, la mayoría de las cuales con propósitos o fines no pensados en un simple buscador. Es aquí donde uno toma conocimiento del verdadero poder y valor de la información.

Espero que les sirva!
Cómo evitar SQL Injection + Prepared Statements en PHP
Luego de escribir la serie de artículos sobre hacking usando SQL Injection, creo que es tiempo de escribir sobre algún método de prevención >=)

Como se puede observar en cualquier bibliografía "decente" sobre SQL Injection, existen básicamente 3 formas de prevenir la inyección, las cuales están muy bien descriptas en el artículo de OWASP SQL Injection Prevention Cheat Sheet. Las opciones son:

- Prepared Statements: Los prepared statements son sentencias pre-compiladas, en las cuales se indica qué parámetros serán ingresados por el usuario. De esta forma podemos indicarle al DBMS cuál es el código a ejecutar y cuáles serán las variables. Esto permite que el motor distinga la sentencia a ejecutar de los datos de entrada y así evitar que el usuario agregue sentencias SQL.
Además de la obvia ventaja de prevenir las inyecciones, éstos permiten mejorar el tiempo de ejecución si la misma sentencia se utiliza más de una vez. Cuando armamos una sentencia SQL, el motor de base de datos analiza, compila y optimiza la forma en que la ejecutará, por ello, si la armamos una sola vez y la ejecutamos varias veces (ya sea utilizando los mismos o diferentes parámetros), el tiempo de ejecución disminuirá considerablemente.

- Stored Procedures: Los stored procedures son más conocidos que los prepared statements entre los desarrolladores debido a su uso extensivo en la programación que interactua con BDs. Se escriben procedimientos en el lenguaje del DBMS (los cuales se almacenan en la base de datos) y desde el código del programa se llaman estos procedimientos con las variables ingresadas por el usuario como los parámetros. De esta forma, el DBMS puede distinguir correctamente variables de código.
Los stored procedures son tan eficaces como los prepared statements, pero hay que tener cuidado de no utilizar los parámetros para armar sentencias, porque estaríamos anulando la defensa.

- Escapar todo dato ingresado por el usuario: probablemente ésta sea la forma más difundida de prevenir SQL Injection y la cual se cita en mucha bibliografía, pero también es la menos recomendada. La idea es que cada vez que el usuario ingrese datos que utilizaremos en una sentencia, escapemos los caracteres especiales (como comillas simples o dobles, barras invertidas "\", o caracteres de comentario "--" o "#", etc) para que el dato sea un solo string y el motor de BD no lo confunda con código a ejecutar. Un ejemplo de función que permite escapar los caracteres especiales, cuando utilizamos php + mysql, es mysql_real_escape_string. Recuerden que esta función no es mágica y se puede evitar como expliqué en el artículo SQL Injection avanzado: consultas simplificadas, file inclusión, ejecución remota y más! en la sección "No puedo usar comillas... no importa!".
Cómo explican en la página de OWASP citada anteriormente, este técnica es frágil y se debe utilizar sólo cuando ya contamos con código inseguro y reescribirlo utilizando prepared statements requeriría un costo inaceptable. Todo desarrollo que se comience de cero debe utilizar prepared statements o stored procedures.


Librería PDO para PHP

Como ya les he mencionado varias veces, soy fan del desarrollo en PHP, por lo cual me expandiré un poco sobre cómo prevenir SQL Injection en este lenguaje. Mi opción favorita para tal caso es utilizar prepared statements, porque mi filosofía es "dejar la base de datos, para almacenar datos", lo cual elimina a los stored procedures como opción. Además de esta forma el código es portable para ser utilizado con distintos motores de base de datos.

En fin, para lograr el objetivo utilizaremos la librería PDO. Como lo describen en la documentación oficial de PHP, PDO (PHP Data Objects) es una interfaz ligera para acceder a bases de datos, la cual permite abstraernos del motor de base de datos, utilizando siempre el mismo conjunto de funciones para acceder a los datos. Esto hace que el código sea portable a cualquiera de las bases de datos soportadas por PDO, entre las que se encuentran las más importantes (MySQL, SQL Server, Postgre, Oracle, SQLite, Informix, Firebird, etc).

PDO no fuerza al usuario a utilizar prepared statements, pero ofrece una serie de funciones para el manejo de los mismos. En ellas se puede utilizar tipado fuerte, lo cual agrega mayor seguridad al especificar el tipo de los datos esperados.


Utilizar los Prepared Statements de PDO

Los prepared statements de PDO son fáciles de utilizar, aunque al principio pueden resultar un poco molestos debido a que se debe escribir un poco más de código al ejecutar consultas.
Veamos una comparación de las funciones que más se utilizan al acceder a la base de datos. Tomo como ejemplo las funciones de MySQL, aunque como saben, PDO funciona con casi cualquier base de datos.

Conexión con la base de datos:
$db = mysq_connect('localhost', 'tester', '123456');
mysql_select_db('test', $db);

Para conectarnos utilizando PDO debemos crear un objeto PDO indicando el tipo de la base de datos a utilizar, el host, el nombre de usuario y el password de la siguiente manera:
$db = new PDO("mysql:host='localhost';dbname='test'", 'tester', '123456');
Consulta a la base de datos:
mysql_query("SELECT * FROM content WHERE id=".$_GET['id'], $db)

Esta es la parte más sensible donde debemos evitar utilizar cualquier parámetro ingresado por el usuario directamente en el string de consulta. Primero preparamos la consulta que debemos ejecutar, indicando con : cuáles son las variables ingresadas por el usuario:
$stmt = $db->prepare("SELECT * FROM content WHERE id= :id");
Luego vinculamos la variable ingresada por el usuario con la utilizada en la consulta, indicamos el tipo, y finalmente ejecutamos la consulta.
$stmt->bindParam(":id", $_GET['id'], PDO::PARAM_INT);
$stmt->execute();
Las 2 llamadas anteriores se pueden unificar en una sola, utilizando un arreglo asociativo en la función execute:
$stmt->execute(array(":id" => $_GET['id']));
La única desventaja de utilizar esta forma de bind es que todos los parámetros son tratados como PDO::PARAM_STR (es decir, strings).
Obtener una fila en un arreglo:
mysql_fetch_array($db);

La forma de obtener filas es muy similar a la llamada anterior, lo que debemos ejecutar es lo siguiente:
$stmt->fetch();
Al igual que mysql_fetch_array, el fetch de PDO obtiene por defecto un arreglo con ambos índices: asociativo y numérico. Si deseamos obtener sólo el arreglo asociativo, podemos utilizar el parámetro PDO::FETCH_ASSOC. En caso de desear uno con índices numéricos el parámetro es PDO::FETCH_NUM:
$stmt->fetch(PDO::FETCH_ASSOC) o $stmt->fetch(PDO::FETCH_NUM);
Además contamos con una función que retorna todas las filas resultantes de la ejecución de la consulta:
$stmt->fetchAll();
Ver errores:
mysql_error($db);

En el caso de prepared statements, los errores se obtienen con errorInfo:
$stmt->errorInfo();
el cual retorna un arreglo que contiene:
0 código de error SQLSTATE
1 código de error
2 mensaje de error

Bueno, con eso cubrimos las funciones básicas de acceso a la base de datos, aunque existen unas cuantas más y las pueden ver en el manual The PDOStatement class.


Conclusión

Prevenir las inyecciones SQL es muy sencillo utilizando prepared statements y es sólo cuestión de ser lo suficientemente responsable (y consciente) a la hora de escribir consultas. Una vez que uno se acostumbra, la escritura del código sale de forma natural y esas "líneas de más" no resultan tan molestas. Recuerden que esto les salvará muchos dolores de cabeza y se sentirán tranquilos de que no los "hackearan" utilizando esta técnica.
Write safely! =)


Referencias

- SQL Injection Prevention Cheat Sheet
- Introduction to PHP PDO
- Prepared statements and stored procedures (Prepared statements and stored procedures)
- PHP manual PDO
- Prepared Statements
Mounting lvm partitions: get into the dark depths of disk storage
Buenas amigos, hoy traigo este artículo desde las oscuras tierras donde moran los sysadmins. Producto de romperme la cabeza para lograr un mayor entendimiento sobre cómo funciona un sistema de archivos. Antes que nada debo excusarme por mi escasa actividad en el blog, últimamente mi tiempo se escapa desarrollando Web (PHP+MySQL+HTML+CSS+JavaScript), mi trabajo en seguridad, estudiando Java para tratar de certificar este año, y lo que me resulta más interesante: incursionando en virtualización de servidores utilizando KVM sobre host CentOS. Varias veces pensé en escribir sobre virtualización en KVM, pero el tema es tan amplio que no sé cómo encararlo. Tal vez debería empezar con mini-artículos sobre temas puntuales, como este que escribo, pero me falta tiempo. Prometo más adelante abordar el tema...


Breve intro

Para experimentar con virtualización tomé la decisión de utilizar CentOS, clon a nivel binario de RedHat, y utilizar KVM en favor de Xen. Para las máquinas virtuales decidí utilizar volúmenes lógicos de LVM como discos virtuales en lugar de archivos de imagen. No voy a explicar en este artículo las ventajas de utilizar volúmenes lógicos así que vamos directo a lo técnico...


Necesito un backup de mi máquina virtual

(Si necesitas un tutorial de LVM no lo vas a encontrar en este artículo, Google it!, hay muchos buenos tutoriales dando vueltas: 1, 2, 3)

Necesito hacer un backup de una máquina virtual cuyo disco es un volumen lógico y no un archivo de imagen. Además necesito mantener la máquina virtual en ejecución. Es decir, hacer un backup online. No hay problema! Una de las ventajas de LVM es que permite tomar snapshots de volúmenes lógicos, una especie de "instantánea" del contenido del volumen.

En la figura superior vemos un esquema de ejemplo de un disco. Una de las particiones se marca como tipo 8e y queda disponible como espacio para volúmenes lógicos. Supongamos que el volumen lógico asignado a la vm se llama "vm01" y pertenece al grupo de volúmenes lógicos "vm_group". Para crear un snapshot del volumen lógico utilizamos "lvcreate":

lvcreate --size 16G --snapshot --name vm01-snapshot /dev/vm_group/vm01

Donde --size indica el tamaño máximo del volumen, --name indica el nombre del volumen lógico correspondiente al snapshot y el último parámetro indica la ruta al dispositivo. El resultado es una copia del volumen en un instante determinado.


Listo el backup!

Eso es todo, aunque... Si el guest posee una base de datos, se debe hacer un lock para que cualquier aplicación en ejecución no escriba en tablas, y además se debe hacer un flush para escribir a disco todos los registros que se encuentran en memoria. Esto es necesario para que la base de datos quede en un estado consistente al momento de tomar el snapshot. Se puede hacer utilizando la herramienta ShellSQL (en CentOS se debe agregar el repositorio RPMforge para instalarla)


Que hago con el backup?

Una vez que tenemos la snapshot seguramente queremos moverla a un servidor de backup. Para esto necesitamos montar la imagen y copiarla al servidor de backup utilizando ftp (quise decir sftp, ver este artículo) o inclusive scp.

En Linux no es posible montar una partición LVM (tipo 8e), sólo es posible montar volúmenes lógicos. Esto es fácil si el volumen lógico posee un sistema de archivos como ext2, ext3 o NTFS. Los problemas surgen cuando el volumen lógico (partición lógica) contiene varias particiones en lugar de un sistema de archivos (las cosas se vuelven algo confusas).

¿Por qué un volumen lógico tendría varias particiones? Simple, si es utilizado por una máquina virtual tiene al menos una partición, ya que todo sistema operativo necesita al menos una o varias particiones para funcionar (/boot, /, /var, /home, etc.)

Si tratamos de montar un volumen lógico particionado obtendremos el siguiente resultado:

hfs: unable to find HFS+ superblock

Significa que mount no encuentra ningún sistema de archivos, ya que para encontrar un sistema de archivos habrá que buscarlo en la tabla de particiones. Recordemos que lo que para el host (el volumen lógico) es una partición, para el guest virtual es un dispositivo. El resultado es que tenemos particiones dentro de una partición lógica :S

Montar una partición que se encuentra dentro de un volumen lógico

Gracias a esta guía pude lograr lo que necesitaba. Primero veamos qué particiones contiene el volumen lógico correspondiente al snapshot:
[root@myserver ~]# fdisk -l -u /dev/vm_group/vm01-snapshot

Disk /dev/vm_group/vm01-snapshot: 10.4 GB, 10485760000 bytes
255 heads, 63 sectors/track, 1274 cylinders, total 20480000 sectors
Units = sectors of 1 * 512 = 512 bytes

Device Boot Start End Blocks Id System
/dev/vm_group/vm01-snapshot1 * 63 20450744 10225341 7 HPFS/NTFS

Se observa que el volumen lógico posee una única partición NTFS. El parámetro -u se utiliza para que las unidades sean en sectores y no en cilindros. Esta partición se puede montar si le indicamos a mount que el volumen lógico vm01 es un loopback device y le pasamos un offset (medido en bytes) al comienzo de la partición deseada.
El sistema de archivos NTFS comienza en el sector 63 (se observa en la captura anterior) y cada sector posee 512 bytes. Por lo tanto el offset es 32256.
Con los siguientes parámetros logramos montar la partición deseada:
[root@myserver ~]# mount -o loop,offset=32256 -t ntfs /dev/vm_group/vm01-snapshot /media/vm01-snapshot/
The disk contains an unclean file system (0, 0).
The file system wasn't safely closed on Windows. Fixing.

Voila! Tenemos la partición montada!
[root@myserver ~]# la /media/vm01-snapshot/
total 1310609
drwxrwxrwx 1 root root 8192 Feb 16 07:39 Archivos de programa
-rwxrwxrwx 1 root root 0 Feb 3 11:32 AUTOEXEC.BAT
-rwxrwxrwx 1 root root 4952 Aug 24 2001 Bootfont.bin
-rwxrwxrwx 1 root root 211 Feb 3 11:29 boot.ini
-rwxrwxrwx 1 root root 0 Feb 3 11:32 CONFIG.SYS
drwxrwxrwx 1 root root 4096 Feb 3 11:37 Documents and Settings
-rwxrwxrwx 1 root root 536399872 Mar 30 12:46 hiberfil.sys
-rwxrwxrwx 1 root root 0 Feb 3 11:32 IO.SYS
-rwxrwxrwx 1 root root 0 Feb 3 11:32 MSDOS.SYS
drwxrwxrwx 1 root root 0 Feb 16 07:37 MSOCache
-rwxrwxrwx 1 root root 47564 Aug 3 2004 NTDETECT.COM
-rwxrwxrwx 1 root root 250640 Aug 3 2004 ntldr
-rwxrwxrwx 1 root root 805306368 Mar 30 12:46 pagefile.sys
drwxrwxrwx 1 root root 4096 Feb 3 11:36 System Volume Information
drwxrwxrwx 1 root root 28672 Feb 16 14:48 WINDOWS

Teniendo acceso a la partición podemos copiar el backup del disco donde deseamos (usando scp o sftp).

Espero que les sirva!
Teclado loco en Ubuntu
Escribo este post porque perdí toda la mañana del domingo renegando con esto y tal vez a alguien le sirva la solución:

El día anterior necesitaba trabajar en mi notebook y tuve la idea de usar los periféricos de la PC (monitor, teclado y mouse) para estar más cómodo. Todo funcionó perfecto, salvo que tuve que ajustar un poco la frecuencia de refresco para que se adapte mejor al monitor de 17" de la PC.

El problema apareció hoy cuando quise volver a utilizar mi notebook sin los periféricos de la PC. Al iniciar gnome, luego de loguearme, el teclado funcionaba de manera extraña: la tecla 'u' escribía un '4'; la 'i' un '5'; la 'o' un '6'; etc. Pero la mitad izquierda del teclado funcionaba perfectamente.

Al darme cuenta que el teclado funcionaba perfectamente antes de iniciar gdm (ya que mi contraseña para ingresar funcionaba) comencé a investigar un poco la configuración de gnome. Encontré que en directorio:
~/.gconf/desktop/gnome/peripherals/keyboard/.../0/
Existía un archivo llamado:
%gconf.xml
El mismo (lamentablemente lo borré por lo que no tengo el contenido exacto) tenía una única línea significativa que indicaba que la tecla 'Block Num' estaba activada. He aquí el problema, mi notebook no posee teclado numérico, por lo tanto no tiene esa tecla. Lo que sucedía era que las teclas entre la 'n' y la 'p' funcionaban como teclado numérico.


La solución fácil

Borrar el archivo "%gconf.xml" y el reiniciar gdm. El teclado vuelve a la normalidad.
Automatización de transferencias por SFTP
Ante la necesidad de transferir archivos entre servidores de forma automatizada (sin intervención humana) recordé la excelente opción que otorga openSSH en su suite de herramientas: autenticación mediante clave pública/privada.
Dado que en un script es bastante feo lograr la interacción requerida por SFTP para autenticar un usuario (comportamiento default), contar con esta opción es extremadamente útil. Sí, no es imposible realizar la autenticación de forma interactiva, algo que está muy bien explicado en varias páginas e hice en un script de backup hace un par de años, pero es mucho más elegante usar la otra opción.

La idea de este artículo es explicar en pocos pasos cómo configurar tanto el cliente como el servidor SSH para utilizar autenticación de clave pública/privada. Además les mostraré cómo escribir un script que, utilizando esta autenticación, sirve para enviar archivos de forma automática.
Como si esto fuera poco (?!), también les mostraré como escribir un script que utilice autenticación interactiva, a través de la herramienta expect. Todo claro?, entonces "a darle átomos".


Generación del par de claves pública/privada

Como la autenticación será a través de estas claves, necesitaremos generar un par por cada usuario que utilicemos. La generación es muy simple, gracias a la herramienta ssh-keygen (si suena a generador de serials para crackear aplicaciones). Las claves se pueden generar en cualquier máquina, no necesariamente el servidor o el cliente. El comando a ejecutar es:
$ ssh-keygen -f id_rsa -t rsa
donde:
-f especifica el nombre del archivo. Se generan dos archivos, la clave privada con el nombre que pasamos en el parámetro, y la pública con el nombre concatenado a .pub
-t especifica el algoritmo que deriva las claves (puede ser RSA o DSA, lo más común es usar RSA, aunque DSA es más seguro).
Ejemplo:
$ ssh-keygen -f id_rsa -t rsa
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in id_rsa.
Your public key has been saved in id_rsa.pub.
The key fingerprint is:
68:82:cb:67:8d:b1:4c:e1:3c:da:22:c0:c3:22:48:c2 demasiadovivo@192.168.1.1
The key's randomart image is:
+--[ RSA 2048]----+
| |
|. |
|.E . |
|* + . . |
|=+. B o S |
|+..* O |
|. = B . |
| . + |
| |
+-----------------+
Cuando intentemos generar las claves, el programa preguntará si deseamos asignarle un passphrase para proteger la clave privada. En este caso hay que dar enter porque si asignamos una passphrase, cada vez que deseemos autenticar un cliente, tendremos que poner dicha passphrase a mano, para desbloquer la clave privada... esto no serviría a nuestros propósitos de utilizar un script automático...

Del par generado, utilizaremos la clave pública en el servidor (id_rsa.pub en el ejemplo) y la privada en el cliente (id_rsa).


Configuración del servidor SSH/SFTP

En el servidor lo primero que tenemos que hacer es habilitar las opciones que permiten la autenticación con clave pública/privada, es decir, editando el archivo /etc/ssh/sshd_config (o /etc/sshd_config en otras distros):
RSAAuthentication yes
PubkeyAuthentication yes
Luego debemos copiar la clave pública del cliente en el archivo de claves autorizadas para ese usuario /home/usuario/.ssh/authorized_keys:
cat id_rsa.pub >> /home/usuario/.ssh/authorized_keys

Conexión desde el cliente

En el cliente tenemos dos alternativas: pasar la clave privada como parámetro de la llamada sftp, o copiar la clave privada en /home/usuario/.ssh/. En el segundo caso, al intentar loguearnos en el servidor remoto, sftp utilizará por default la clave que encuentre en el directorio ssh del home, es decir, podemos ejecutar:
sftp <usuario>@<hostname>
por ej: sftp demasiadovivo@192.168.1.2
Si necesitamos pasar la clave por parámetro (algo en muchos casos necesario porque el usuario que ejecuta el script puede no tener home), debemos ejecutar el siguiente comando:
sftp -o "IdentityFile=<clave-privada>" <nombre-usuario>@<hostname>
por ej: sftp "IdentityFile=/datos/id_rsa" demasiadovivo@192.168.1.2
Me costó bastante encontrar la forma de armar este último comando, es necesario leer bien el manual de ssh_config =S


Automatizar copiado de archivos

Como les comenté, la razón por la cual necesitaba lo anterior es para automatizar el envío de archivos de un servidor a otro. Una vez que tenemos la configuración necesaria, en el servidor1 o servidor cliente del servidor2, podemos colocar el siguiente script:
#!/bin/bash

CMDFILE="sftp-commands"

echo "put <path-archivo>
bye" > $CMDFILE
sftp -o "IdentityFile=<clave-privada>" -b $CMDFILE <nombre-usuario>@<hostname>
rm $CMDFILE
Como pueden observar, el script es muy simple. Lo que hace es usar sftp en modo batch (-b $CMDFILE) para poder enviar los comandos de forma no-interactiva, ya que en el comportamiento default sftp espera recibir de la standar input los comandos a ejecutar en el servidor.
En resúmen, crea un archivo temporal cuyo nombre es sftp-commands, en el cual coloca los comandos a ejecutar (por ejemplo, el put de un archivo). Luego llama a sftp utilizando autenticación de clave pública/privada y el modo batch, donde le pasa el archivo recién creado como input. Finalmente borra el archivo temporal.


Automatización, opción 2

El segundo script que les quería mostrar es cómo hacer lo anterior, pero con autenticación interactiva, algo un poco más sucio, pero igualmente útil:
#!/usr/bin/expect

spawn sftp <usuario>@<hostname>
expect "password:"
sleep 3
send "<mi-contraseña>\r";
expect ">"
send "put <path-archivo>\r"
expect ">"
send "bye\r"
Como el script es interactivo, si bien no hace falta que ingresemos manualmente ningún comando, éste se verá en la standar output, así que cuando llamemos al script es necesario redirigir la salida a /dev/null. Por ejemplo:
$ ./sftp-command &>/dev/null

Referencias

- How can I automate an SFTP transfer between two servers?
- sftp Linux man page
- ssh_config Linux man page