OpenVPN y Samba sobre openSUSE 10.3

lunes, 31 de diciembre de 2007
Este fin de semana estuve "jugando" con unos softwares bien potentes, OpenVPN y Samba. OpenVPN sirve para crear redes virtuales privadas entre equipos que fisicamente estan separados (no estan en la misma red ni en el mismo dominio). De esta manera se pueden conectar equipos o redes enteras unas con otras, por ejemplo para unir sucursales de una misma organización y compartir información por un canal seguro, o comunicar empresas con proveedores, etc.

OpenVPN utiliza OpenSSL para agregar la capa de seguridad al sistema a través de la encriptación, haciendo uso de llaves simétricas o a través de emisión de certificados digitales y llaves asimétricas (el segundo método es lejos mejor).

A través de OpenVPN podemos conectar equipos con distintos sistemas operativos, ya que tiene versiones para varios (linux, windows, etc.) y corre tanto en modo servidor como en modo cliente (se pueden ejecutar los dos modos al mismo tiempo en el mismo equipo en procesos distintos).

Samba es un software que sirve para compartir recursos en una red, autenticar usuarios, controlar acceso a recursos, tiene compatibilidad con active directory y un laaaargo etc. Mediante Samba podemos compartir recursos en equipos que corren distintos sistemas operativos, por ejemplo podemos compartir una carpeta en linux y desde windows lo podriamos ver sin problemas, o al revés, desde windows podríamos compartir una carpeta (a traves de samba o de windows) y desde linux lo podríamos ver también.

La unión de estos dos sistemas lo encuentro especialmente poderoso, por un lado creamos una red con equipos distantes y por otro lado aprovechamos los recursos compartiendo y manejando, de forma transparente, la seguridad de los datos en la red.

OpenSUSE 10.3 y Mandriva 2008 (los que probé en estos dias) traen todo lo necesario para armar un servidor/cliente OpenVPN y Samba.

En internet se pueden encontrar varias guías para ayudar a configurar OpenVPN y Samba, en mi caso particular me gustó mucho ésta guía para OpenVPN que seguí paso a paso, solo cambie cosas muy particulares para que funcionara en openSUSE 10.3. Debo recalcar que es muy buena y no tiene caso volver a redactar algo como lo que sale ahí en mi blog, está para seguir paso a paso y es casi imposible equivocarse.

Sobre Samba, leí muchas guías en Internet, ninguna me sirvió. Lo fácil es levantar el servidor usando YAST, pues en YAST -> Servicios de red -> Servidor Samba le damos siguiente siguiente a todo y ya está.
Incluso te da la opción de abrir el puerto en el cortafuegos (genial, todo automático y a puros clicks).

El problema que tuve fue al tratar de compartir recursos, YAST te da la opción, pero cuando quería acceder a las carpetas compartidas no podía, ingresaba smb:/ en konqueror y la red aparecía, las carpetas también pero al acceder salen errores como "el recurso o carpeta no existe", siendo que tenía definido que la carpeta estaba para compartir con todos sin pedir password ni nada. Probé desde el módulo de Samba en KDE, lo mismo, los mismos errores. Perdí muchas horas tratando, conclusión, las herramientas que vienen con openSUSE no lo hacen del todo bien o a mi me faltó marcar/desmarcar alguna opción.

Al final lo solucioné a mano (como en la vieja escuela). Samba lee un archivo de configuración que se llama smb.conf que está en /etc/samba. Este archivo define una configuración global a base de directivas del tipo variable = valor. La parte global YAST la maneja a la perfección, pero por experiencia propia recomiendo agregar los recursos de red a compartir a mano.

En este caso particular de OpenVPN y Samba, hay una cosa que hay que tener en cuenta, si configuran Samba (suponiendo que los recursos estan disponibles en la red, todo correcto) y OpenVPN, desde el cliente OpenVPN NO tendrán acceso a los recursos Samba.

Lo anterior es porque OpenVPN crea una sub red definida en 10.8.0.0/24 desde la cual asigna una ip a cada máquina dentro de ese rango y Samba solo permite compartir recursos en la sub red del servidor (en mi caso 192.168.0.0/24), por lo tanto aunque las máquinas estén en red, no podrán acceder a los recursos.

Para solucionar esto solo hay que agregar una linea de texto a la configuración global de samba (smb.conf):
hosts allow = 192.168.0.0/24 10.8.0.0/24 127.0.0.1

y en el caso de que el servidor Samba y el servidor OpenVPN estén corriendo en la misma máquina se agrega la siguiente linea:
interfaces = 192.168.0.0/24 10.8.0.0/24

Luego se reinicia el servidor Samba (#service smb restart) y los clientes de la VPN podrán acceder a los recursos sin problema.

Ahora, cómo agregamos recursos a Samba ? pues la verdad es muy simple y les mostraré 3 ejemplos, en los cuales compartiremos 3 carpetas pero usando distintas directivas para definir permisos, control de acceso ,etc.

[compartido]
comment = Directorio de acceso general
path = /home/metalklesk/Documents/Compartido/General
read only = No
guest ok = yes
browseable = yes

Con esa configuración, el recurso está disponible para cualquiera y tiene permisos de escritura y lectura.
  1. La primera linea define el nombre del recurso, con éste nombre se identifica en la red.
  2. Lo segundo es solo un comentario y puede ser omitido.
  3. El path define el directorio del recurso que se quiere compartir.
  4. Read only = no significa que estamos dando permisos de lectura y escritura al recurso.
  5. guest ok = yes significa que permitimos que cualqueira entre y vea el recurso.
  6. browseable = yes significa que el recurso se puede ver por cualquiera y está disponible.

[restringido]
comment = Directorio de acceso restringido
path = /home/metalklesk/Documents/Compartido/Restringido
read only = No
browseable = yes
security = shared
valid users = klesk metalklesk


Con esta configuración el recurso solo estará disponible para los usuarios klesk y metalklesk, para cualquier otro usuario el acceso al recurso será denegado. Esto se define a través de la directiva valid users = klesk metalklesk.

[restringido2]
comment = Directorio de acceso restringido
path = /home/metalklesk/Documents/Compartido/Restringido2
read only = No
browseable = yes
security = shared
invalid users = metalklesk


Esta configuración permite el acceso al recurso a todos los usuarios de la red excepto al usuario metalklesk mediante la directiva invalid users = metalklesk.

Bueno, pero y como se agregan usuarios a Samba ? pues muy fácil, se utiliza el comando:
smbpasswd -a usuario

y se le ingresa una contraseña y listo.

Ya con esto podrán compartir carpetas o recursos especificando algunas reglas para cada uno de ellos. Existem muchas mas combinaciones y mas directivas, se pueden remitir al manual de Samba para conocerlas todas y lo recomiendo.

A continuación les dejo mi archivo smb.conf completo por si les sirve.

# smb.conf is the main Samba configuration file. You find a full commented
# version at /usr/share/doc/packages/samba/examples/smb.conf.SUSE if the
# samba-doc package is installed.
# Date: 2007-09-22
[global]
printing = cups
printcap name = cups
cups options = raw
map to guest = Bad User
include = /etc/samba/dhcp.conf
logon path = \\%L\profiles\.msprofile
logon home = \\%L\%U\.9xprofile
logon drive = P:
usershare allow guests = Yes
usershare max shares = 100
security = share
restrict anonymous = no
domain master = no
preferred master = no
max protocol = NT
acl compatibility = win2k
ldap ssl = No
server signing = Auto
SO_RCVBUF=8192 SO_SNDBUF=8192
socket options = TCP_NODELAY
hosts allow = 192.168.0.0/24 10.8.0.0/24 127.0.0.1
interfaces = 192.168.0.0/24 10.8.0.0/24

[homes]
comment = Home Directories
valid users = %S, %D%w%S
browseable = No
read only = No
inherit acls = Yes

[users]
comment = All users
path = /home
read only = No
inherit acls = Yes
veto files = /aquota.user/groups/shares/
browseable = yes
public = yes

[groups]
comment = All groups
path = /home/groups
read only = No
inherit acls = Yes

[printers]
comment = All Printers
path = /var/tmp
printable = Yes
create mask = 0600
browseable = No

[print$]
comment = Printer Drivers
path = /var/lib/samba/drivers
write list = @ntadmin root
force group = ntadmin
create mask = 0664
directory mask = 0775

[compartido]
comment = Directorio de acceso general
path = /home/metalklesk/Documents/Compartido/General
read only = No
guest ok = yes
browseable = yes

[restringido]
comment = Directorio de acceso restringido
path = /home/metalklesk/Documents/Compartido/Restringido
read only = No
browseable = yes
security = shared
valid users = klesk metalklesk

[restringido2]
comment = Directorio de acceso restringido
path = /home/metalklesk/Documents/Compartido/Restringido2
read only = No
browseable = yes
security = shared
invalid users = metalklesk

Es muy probable que les suceda que no puedan acceder a los recursos desde un cliente (windows o linux), deben fijarse en los permisos de usuario de las carpetas o directorios que estén compartiendo. Por ejemplo la carpeta Documents en openSUSE no puede ser accedida por otro usuario que no sea el dueño y el root, por lo tanto aunque configuren todo bien en Samba, no podrá ser accesado por un cliente. O modifican los permisos de la carpeta o escojen otra.

Y como siempre unas capturas de pantalla. La primera muestra a openSUSE 10.3 corriendo un servidor Samba y OpenVPN y mandriva como cliente de ambos.


La siguiente muestra el diálogo de autenticación que sale al intentar entrar a la carpeta restringida


La siguiente muestra que efectivamente las máquinas están conectadas en red, mediante un simple ping a la ip del cliente (ip asignada por el servidor OpenVPN)


La siguiente muestra un cliente Windows conectado a la VPN


La siguiente muestra como Windows puede ver los recursos compartidos en openSUSE 10.3 mediante samba


En las imágenes anteriores se ve que las pruebas fueron realizadas con máquinas virtuales, esto no es ninguna trampa, efectivamente están en red a través de OpenVPN. Además probé con el equipo de un amigo y también funcionó.

Espero les sirva.

saludos !!!

Pebble, un blog desarrollado en java sobre Glassfish V2 en openSUSE 10.3

sábado, 29 de diciembre de 2007
Pebble es un sistema blog liviano que soporta multi usuarios, muti blogs, cada uno puede tener uno o mas blogs. Maneja roles de usuario como administrador, dueño de un blog, etc.

Trae varias caracteristicas bien interesantes, como por ejemplo:
  • Permite subir archivos de cualquier tipo para compartir con los lectores.
  • Tiene un editor de texto increible, es sumamente configurable.
  • Trae soporte para varios idiomas.
  • Puedes cambiar el theme por usuario.
  • No requiere de una base de datos.
  • etc.
Pueden ver todas sus características en el sitio web oficial.

Existen muchos sistemas de blog públicos como Blogger o Wordpress, entonces para que interesarse en otro mas ? que puede aportar pebble a nuestra vida personal y/o profesional ?

Pues puede aportar mucho, es open source asi es que de licencias no hay que preocuparse, se puede adecuar a nuestras necesidades pues se tiene el código fuente.

Es muy útil para crear blogs al interior de una organización (empresa, universidades, colegios, cooperativas, etc.) como apoyo al desarrollo de proyectos y/o tareas o actividades, por ejemplo:
  • Compartir información útil.
  • Dar avisos importantes de eventos al interior de la organización.
  • Apoyar el desarrollo de memorias de título ya que se pueden ir subiendo los informes de avances, etc., pudiendo comentar y aportar positivamente.
  • etc.

Pebble es muy liviano y viene listo para su uso, no es necesario recompilar para configurarlo. Funciona sobre cualquier servidor de aplicaciones que soporte JSP 2.0/Servlet 2.4, como por ejemplo Tomcat 5.5.x o Glassfish, entre otros.

Lo primero que hay que hacer es descargarlo, yo descargo siempre la última versión mas actual para ir probando, quizas ustedes prefieran la última versión estable.

Una vez descargado simplemente hay que descomprimir el zip como se ve en la imagen a continuación:

Dentro de la carpeta de Pebble se puede encontrar el código fuente y un archivo llamado pebble.war, el cual no es mas que un archivo zip con el proyecto precompilado. Si no quieren cambiar nada pueden dejarlo tal cual, en mi caso quize cambiar la configuración de pebble para que soporte multi blogs, para eso sacamos una copia del archivo pebble.war y lo renombramos a pebble.zip, luego descomprimimos el zip y entramos a la carpeta WEB-INF y abrimos con un editor de texto el archivo llamado pebble.properties.

Dentro de ese archivo se pueden configurar un par de cosas, como por ejemplo el tamaño máximo para archivos que se quieran subir para compartir con los lectores del blog o dar/quitar soporte multi blog.
En este caso interesa modificar lo segundo (criterio mio, si quieren cambiar algo mas pueden hacerlo). Si se fijan, hay una variable que se llama multiblog y está con valor false, hay que cambiarlo a true.


Una vez listo guardamos y cerramos. Ahora hay que reconstruir el archivo war, para esto simplemente seleccionamos todo y creamos un archivo zip como ven a continuacion:


y renombramos el zip a war.


Ya tenemos Pebble listo, ahora hay que moverlo a un servidor de aplicaciones. En este caso utilizaremos Glassfish.

Si aún no tienes Glassfish instalado y/o configurado entonces date una vuelta por ésta entrada donde explico paso a paso como tener Glassfish correctamente configurado.

Hay que mover el archivo pebble.war a la carpeta /domains/domain1/autodeploy y levantamos el servicio (si estaba arriba entonces no hay que hacer nada mas), al tener el servicio levantado, Glassfish creará un archivo que se llamará pebble.war_deployed.



Una vez copiado el archivo war al servidor de aplicaciones podemos comenzar a utilizarlo, en un navegador escriban
http://localhost:8080/pebble/

y podrán utilizarlo.


Lo primero que deben hacer es entrar como administrador, por default el nombre de usuario es username y la contraseña es password (se puede cambiar).

Una vez logueado como administrador ya pueden crear usuarios, asignar roles, crear blogs y asignárselos a usuarios.


Espero les sea de utilidad como a mi.

saludos !!!

Instalación de Glassfish V2 en openSUSE 10.3

viernes, 28 de diciembre de 2007
En mi blog, varias veces he hablado de Glassfish, lo he utilizado en uno de los ejemplos, pero siempre bajo NetBeans 6.0. En un ambiente de producción no tiene sentido utilizar un IDE de desarrollo, de hecho el servidor no deberia tener mas que el SO, un par de programas, un entorno grafico simple (incluso algunos prefieren que el servidor NO tenga entorno gráfico, por seguridad), etc., lo minimo posible.

Instalar glassfish es bastante sencillo, lo primero es descargarlo acá, (necesitan tener java instalado y configurado previamente) luego ejecutar en consola (no es necesario ser ROOT para esto) lo siguiente:
java -Xmx256M -jar glassfish-installer-v2-b58g-ml.jar

les aparecerá una ventana con la licencia, presionan accept y con eso comenzará la instalación la cual finalizará dejando una carpeta llamada glassfish/ en el directorio en el cual ejecutaron el comando anterior.


Luego hay que dar permisos de ejecución al ant que viene embedido en glassfish, para esto ejecutamos en consola lo siguiente (ojo con el directorio en el que tengan glassfish):
chmod -R +x bin/glassfish/lib/ant/bin/

luego entren al directorio de glassfish (a la raiz de éste):
cd bin/glassfish

y escriben lo siguiente:
./lib/ant/bin/ant -f setup.xml

Con el comando anterior se va a configurar el servidor de aplicaciones. Dentro de setup.xml están los puertos que se utilizarán (ademas de otras cosas como el nombre de usuario del administrador y su contraseña), por ejemplo el 4848 que es para entrar a la consola del administrador, 8080 para las aplicaciones web, 8181 para las aplicaciones web seguras, etc. Si quieren cambiar la configuracion que viene por default pueden editar ese archivo.

Una vez ejecutado el comando anterior ya estamos listos para levantar el servidor de aplicaciones.

Para levantar el servidor de aplicaciones glassfish crearemos un simple script, cuyo contenido será el siguiente:
#!/bin/bash

#ingrese el directorio donde se encuentra el servidor de aplicaciones glassfish
GLASSFISH_HOME=/home/metalklesk/bin/glassfish

#ingrese el nombre de usuario del administrador del servidor de aplicaciones glassfish
AS_ADMIN_USER=admin

export AS_ADMIN_USER
export PATH=${GLASSFISH_HOME}/bin:$PATH

asadmin start-domain domain1

Deben modificar el valor de la variable GLASSFISH_HOME al que corresponda, deben colocar el directorio donde se encuentra glassfish en su equipo y lo mismo con la variable AS_ADMIN_USER, si modificaron el nombre de usuario del administrador en el archivo setup.xml, debe reflejarse en el script.

Al script pueden darle el nombre que quieran, y al final permisos de ejecución:
chmod +x iniciar-glassfish.sh

Para levantar el servidor de aplicaciones simplemente ejecutamos el script previamente creado y listo.
./iniciar-glassfish.sh

Ahora para comprobar que el servidor esta corriendo, abrimos un navegador y colocamos en el buscador:
http://localhost:4848

y con eso se abrirá la consola del administrador, ingresamos el nombre de usuario y contraseña y ya está.


Glassfish trae un ejemplo para probar el funcionamiento del servidor de aplicaciones, se encuentra en el directorio /samples/quickstart y el archivo se llama hello.war, ese hay que copiarlo en el directorio /domains/domain1/autodeploy y listo, con eso la aplicación se va a auto configurar, pueden ver en la consola de administrador que esa aplicación (hello.war) ya esta corriendo.


Como ven la instalación y configuración de glassfish es muy facil de hacer. Una caracteristica que me gusta es que puedes hacer hot deploy, es decir, mientras el servidor de aplicaciones está corriendo puedes hacer deploy sin necesidad de bajarlo antes, no afecta en nada a las otras aplicaciones que puedan estar corriendo sobre glassfish.

Espero les sirva esta simple guía.

saludos !!!

Asignar ip a máquina virtual con VirtualBox OSE 1.5.2 en openSUSE 10.3

lunes, 24 de diciembre de 2007
Ayer me preguntaron en esta entrada sobre como crear una red entre un host y una o mas máquinas virtuales, pero asignándoles a cada una un ip diferente de manera tal que pudiéramos crear servidores pero en máquinas virtuales. Esto suena mas simple de lo que parece.

Con VirtualBox se puede hacer pero el problema principal radica en que éste viene en dos versiones, una GPL y la otra no. La que NO es GPL viene full pero solo para uso personal o testing pero no se puede usar en ambiente comercial debido a las restricciones de su licencia. Sin embargo la versión OSE (Open Source Edition) esta bajo la GPL por lo que podemos hacer lo que queramos con ella.

Con la versión OSE se puede hacer lo mismo que con la otra pero es mas complicado y por ende difícil, pero no imposible.

Despues de leer por la red (googlear no le hace mal a nadie jejejeej) logré hacerlo funcionar, es más, hice un script que hace casi todo automáticamente. Lo hice utilizando VirtualBox OSE 1.5.2 asi es que no se si funcione con versiones anteriores. La descargué de los repositorios de openSUSE 10.3 (se configura en Yast -> Community Repositories, simplemente se marca la casilla que indica software para virtualizar sistemas)

Antes de mostrar el script explicaré a grandes rasgos de que se trata este cuento.

Lo primero que se debe hacer para crear una red entre el host y máquinas virtuales es crear un bridge (puente), el cual se llamará br0. Este puente se utiliza para acceder a Internet y a él se registran las interfaces de red tanto de la máquina real (host) como de las virtuales (guests). Normalmente (cuando no hay bridge entremedio) la máquina real se conecta a Internet mediante la interface de red, generalmente eth0 (esa es en mi caso), pero para lograr la creación de la red todas las máquinas se conectaran a través del bridge.

Una vez que se crea el bridge se deben crear tantas interfaces de red como máquinas virtuales deseemos, una para cada una, las cuales se denominarán tap0, tap1, ... tapN. Estas interfaces son similares a la eth0, la diferencia es que son virtuales. Una vez que se crean se deben registrar al bridge (br0), incluyendo la eth0. Luego se le debe quitar la ip a la eth0, ya que se debe asignar a br0.

Lo anterior es una explicación a groso modo para que entiendan lo que viene a continuación (de todas maneras está comentado). Para que el script funcione correctamente son necesarios dos paquetes fundamentales:
  • bridge-utils
  • uml-utilities
Ambos vienen en el dvd de openSUSE 10.3 (no se si en los anteriores).
Deben configurar las variables definidas en el script para que concuerden con su sistema.
Una vez instalados ejecutan el siguiente script, como usuario root.

#!/bin/bash

#INICIALIZACION DE VARIABLES
#Deben cambiar los valores segun corresponda a su realidad
#seteamos el usuario
USUARIO="metalklesk"

#seteamos la cantidad de maquinas virtuales (guests) que deseamos tener en el host
CANTIDAD_VM=2

#seteamos la interface de red real
INTERFACE_RED_REAL="eth0"

#seteamos el ip del host
IP_HOST="192.168.0.144"

#seteamos la pasarela (gateway)
PASARELA="192.168.0.1"


#CONFIGURACION
#creamos el puente (bridge)
brctl addbr br0

#seteamos la interface de red real para que sea promiscua
ifconfig $INTERFACE_RED_REAL 0.0.0.0 promisc

#insertamos la interface de red real al puente br0
brctl addif br0 $INTERFACE_RED_REAL

#creamos una interface tap para cada guest (maquina virtual),
#los insertamos en el puente y los subimos
CONTADOR=0
while [ $CONTADOR -lt $CANTIDAD_VM ]
do
tunctl -t tap$CONTADOR -u $USUARIO
brctl addif br0 tap$CONTADOR
ifconfig tap$CONTADOR up
let CONTADOR=$CONTADOR+1
done

#asignamos una ip al puente br0 mediante dhcp
#dhclient br0

#asignamos la ip del host al puente br0 de forma manual
ifconfig br0 $IP_HOST

#asignamos la pasarela al puente br0
route add default gw $PASARELA br0

#cambiamos los permisos de /dev/net/tun
chmod 0666 /dev/net/tun

#NOTAS.
#
# Un Bridge es contrario a un firewall, por lo tanto si el firewall esta arriba es posible que por default
# este bloqueando la interfaz br0 (u otra). La solucion mas simple es deshabilitar el firewall, pero no es
# recomendable ya que eso resta seguridad. Entonces lo que hay que hacer es configurar el firewall para que
# permita, a la interfaz br0, salir al exterior (y ser vista desde el exterior y dentro de la red). En el caso
# de openSUSE 10.3 es muy simple de hacer, simplemente hay que abrir con un editor de texto (como root) el
# archivo de configuracion del firewall:

# /etc/sysconfig/SuSEfirewall2

# bajar al final y buscar la linea que dice:

# FW_FORWARD_ALWAYS_INOUT_DEV=""

# En esa linea hay que agregar las interfaces que queremos separadas por espacios, en este caso corresponde al
# bridge br0, la linea queda de la siguiente forma:

# FW_FORWARD_ALWAYS_INOUT_DEV="br0"

# Ya con eso simplemente hay que reiniciar el firewall (desde Yast o desde consola) y listo.
# Lo que resta es configurar la red de la maquina virtual. Desde VirtualBox se crea una maquina virtual, luego
# hay que ir a Settings -> Network -> Adapter0 y cambiar Attached to de "Not attached" a "Host Interface" y en
# Interface Name escribir tap0 o la interfaz que corresponda (depende de cual le quieren asignar a la
# maquina virtual).
# Ya con eso pueden instalar tranquilamente el SO Guest en VirtualBox, luego configurar la red de este dándole una
# ip a mano o a traves de dhcp que esté dentro del rango provisto por la subred. Con todo lo anterior (incluidos
# los comandos de este script) tienen configurado una red con las maquinas virtuales y cada una con su propia ip,
# lo cual es muy util para configurar servidores.

#FIN
Una vez ejecutado el script simplemente creamos una máquina virtual y en Settings -> Network -> Attached to cambiamos de Not attached a Host Interface y en Interface Name colocamos tap0 (o el que queramos asignarle). ya con eso pueden levantar la máquina virtual y dentro configuran la red asignándole un ip estático o a través de dhcp (como en mi caso) dentro del rango de la subred, con lo cual ya tienen su máquina virtual en red con la real (y con la red entera).

A continuación pueden ver una imagen que saqué al mostrar las interfaces de red configuradas después de ejecutar el script (se configuran al ejecutar el script, no es necesario que lo hagan ustedes :)):

A continuación pueden ver una imagen de la parte donde se configura la interface de red de la máquina virtual (explicado anteriormente):

Aquí pueden ver la máquina virtual corriendo, en la cuál se puede ver que tiene una ip asignada (192.168.0.125), además está de servidor de Openbravo 2.35:

y aquí pueden ver que en el host puedo acceder a Openbravo que está en la máquina virtual (fijense en el ip que aparece en el navegador):

y una última imagen para que vean las dos cosas anteriores al mismo tiempo:


PD: se me olvidaba, es más aún, mi router (D-Link 704p) detecta a la máquina virtual como si fuera real:


Espero les sea de utilidad y ya saben, cualquier consulta la hacen comentando.

saludos!!!!

Sistemas distribuidos usando RMI

miércoles, 12 de diciembre de 2007
Los sistemas distribuidos se utilizan generalmente para distribuir carga en diferentes equipos. No es lo mismo que un cluster (existen varios tipos, cada uno con fines distintos), un cluster puede ser solo un caso de un sistema distribuido. En este contexto, uno puede, literalmente, dividir un sistema en partes que se pueden distribuir en distintas máquinas, máquinas reales o virtuales, o dentro de la misma máquina.

Pero y de qué sirve ? para qué dividir un sistema en varias partes ?. Las razones pueden ser variadas, pero seguramente todas tienen que ver con aumentar la velocidad de procesamiento, el agilizar la atención de clientes, disminuir la carga en el servidor principal, etc.

La idea general es que, por un lado se tienen clientes que esperan una atención y por otra un servidor que responde las peticiones, pero antes de responder, las peticiones se distribuyen a distintas máquinas de manera tal que cada una procesa una parte (o todo lo que pide un cliente) mientras que otro procesa otra parte (o a otro cliente). Esto impacta directamente en la velocidad de respuesta ya que no es un servidor el que se encarga de manejar todas las peticiones, sino que en realidad son varios.

Pero y cómo se desarrolla un sistema distribuido ? bueno, depende de la tecnología que utilizemos, pero en general es lo mismo. Un servidor implementa métodos que proveen servicios y se registran en el servidor como un servicio público el cuál puede ser accesado por los clientes.

Las tecnologías mas populares son Remoting de .NET y RMI de Java. También existe CORBA.

En esta instancia les quiero dar un ejemplo de RMI, mostrando los pasos o la "receta" a seguir. Hace un tiempo atrás, en este mismo blog, hice una guía de cómo hacer un sistema de pago online (nada real, solo virtual) al servicio de impuestos internos (SII) utilizando web services. Este ejemplo trata de lo mismo pero utilizando RMI en vez de web services. La base de datos la cambié de Derby a MySQL y el servidor de aplicaciones Glassfish no fue necesario.

En el link de RMI se explica cómo se utiliza pero la verdad para mi no fué tal cual sale explicado ahi, no se explica bien el tema del Stub que es parte escencial para generar un sistema distribuido con Java.

Lo primero que se debe hacer al momento de querer desarrollar un sistema distribuido (o cualquier tipo de sistema, despues de el desarrollo de requerimientos) es definir la arquitectura a utilizar. A continuación les mostraré un ejemplo de ésto:


Como se puede ver, al lado izquierdo se tienen dos clientes y al lado derecho un servidor. Este servidor posee una Interfaz que define los servicios a publicar y al lado se ve un Servicio Remoto, que en éste caso es una clase que implementa los métodos de la Interfaz.

La interfaz debe heredar de la clase Remote de java.rmi, mientras que la clase que implementa debe heredar de la clase UnicastRemoteObject de java.rmi.server.

A continuación se puede ver la interfaz InterfaceRemota:

package org.remoto;

import java.rmi.Remote;
import org.cliente.UsuarioBean;

/**
*
* @author metalklesk
*/
public interface InterfaceRemota extends Remote{

public String obtenerDeuda(String rut) throws java.rmi.RemoteException;

public String pagarDeuda(String rut, String monto) throws java.rmi.RemoteException;

public UsuarioBean obtenerUsuario(String rut) throws java.rmi.RemoteException;
}

y a continuación pueden ver la implementación de los métodos de la interfaz en la clase ServicioRemoto:

package org.remoto;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import org.cliente.UsuarioBean;
import org.logica.LogicaNegocio;

/**
*
* @author metalklesk
*/
public class ServicioRemoto extends UnicastRemoteObject implements InterfaceRemota{

private LogicaNegocio ln;

public ServicioRemoto() throws RemoteException{
ln = new LogicaNegocio();
}

public String obtenerDeuda(String rut) throws RemoteException {
String respuesta = ln.obtenerDeuda(rut);
return respuesta;
}

public String pagarDeuda(String rut, String monto) throws RemoteException {
String respuesta = ln.pagarDeuda(rut, monto);
return respuesta;
}

public UsuarioBean obtenerUsuario(String rut) throws RemoteException {
UsuarioBean usuario = ln.obtenerUsuario(rut);
return usuario;
}
}

La verdad, lo mas sano es separar en multicapas, de manera tal de que cuando se quiera modificar algo, solo afecte a la capa que corresponda siendo transparente para las demás en el sistema.

Lo siguiente es levantar el servicio, para ésto se necesita utilizar el método rebind de la clase Naming del package java.rmi. A continuación pueden ver el código de la clase Servicio que se encarga de ésto:

package org.principal;

import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.configuracion.AccesoConfiguracion;
import org.remoto.ServicioRemoto;

/**
*
* @author metalklesk
*/
public class Servicio {

private ServicioRemoto servicio;

public Servicio() {
try {
servicio = new ServicioRemoto();
} catch (RemoteException ex) {
Logger.getLogger(Servicio.class.getName()).log(Level.SEVERE, null, ex);
System.exit(1);
}
}

public boolean iniciarServicio(){
try {
Naming.rebind((AccesoConfiguracion.getInstance()).getServicioRMI(), servicio);
return true;
} catch (RemoteException ex) {
Logger.getLogger(Servicio.class.getName()).log(Level.SEVERE, null, ex);
return false;
} catch (MalformedURLException ex) {
Logger.getLogger(Servicio.class.getName()).log(Level.SEVERE, null, ex);
return false;
}
}

public boolean pararServicio(){
try {
Naming.unbind((AccesoConfiguracion.getInstance()).getServicioRMI());
return true;
} catch (RemoteException ex) {
Logger.getLogger(Servicio.class.getName()).log(Level.SEVERE, null, ex);
return false;
} catch (NotBoundException ex) {
Logger.getLogger(Servicio.class.getName()).log(Level.SEVERE, null, ex);
return false;
} catch (MalformedURLException ex) {
Logger.getLogger(Servicio.class.getName()).log(Level.SEVERE, null, ex);
return false;
}
}

@Override
public void finalize(){
pararServicio();
}
}

El método rebind recibe dos parámetros, un String con el nombre del servicio (puede ser cualquier cosa) y el objeto que implementa los métodos a publicar.

Ya con eso estamos casi listos por el lado del servidor. Lo siguiente es crear un Stub, que es un archivo .class que se encarga de entablar la comunicación en la red con el cliente. Este Stub se crea usando el comando rmic, en éste caso es:

rmic org.remoto.ServicioRemoto

lo cuál crea el archivo ServicioRemoto_Stub.class. Es importante decir que el comando anterior se hace con los archivos .class, no los .java, no se confundan con eso. Lo otro que es importante es que se fijen en los packages. No vale entrar a la carpeta org/remoto y ejecutar ahí el comando, se debe respetar el package.

Ahora debemos ejecutar:

rmiregistry

y el servicio rmi ya está registrado en el servidor, por default en el puerto 1099 (se puede cambiar pasando el puerto como parámetro al comando anterior).

Ahora solo resta crear el cliente. Lo importante por el lado del cliente son dos cosas.
Primero se debe tener la interfaz InterfaceRemota ya que es el puente de comunicación con el servidor y además se necesita el Stub (el mismo que se creó en el lado del cliente).

Para comunicarnos con el servidor se tiene el siguiente código:

package org.datos;

import org.cliente.*;
import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.configuracion.AccesoConfiguracion;
import org.remoto.InterfaceRemota;

/**
*
* @author metalklesk
*/
public class AccesoDatos {

private static AccesoDatos acceso = new AccesoDatos();
private static boolean instanciado = false;

private InterfaceRemota servicio;

public static synchronized AccesoDatos getInstance(){
if(!instanciado){
instanciado = true;
return acceso;
} else{
System.out.println("No se puede instanciar el acceso a datos mas de una vez");
return null;
}
}

public boolean conectar(){
try {
servicio = (InterfaceRemota)
Naming.lookup("rmi://" + (AccesoConfiguracion.getInstance()).getHost() + "/" + (AccesoConfiguracion.getInstance()).getService());
return true;

} catch (NotBoundException ex) {
Logger.getLogger(AccesoDatos.class.getName()).log(Level.SEVERE, null, ex);
return false;
} catch (MalformedURLException ex) {
Logger.getLogger(AccesoDatos.class.getName()).log(Level.SEVERE, null, ex);
return false;
} catch (RemoteException ex) {
Logger.getLogger(AccesoDatos.class.getName()).log(Level.SEVERE, null, ex);
return false;
}
}

public String obtenerNombreUsuario(String rut){
if(servicio != null){
try {
UsuarioBean ub = servicio.obtenerUsuario(rut);
String nombreCompleto = ub.getNombre() + " " + ub.getApellidoPaterno() + " " + ub.getApellidoMaterno();

return nombreCompleto;
} catch (RemoteException ex) {
Logger.getLogger(AccesoDatos.class.getName()).log(Level.SEVERE, null, ex);
return null;
}

} else {
return null;
}
}

public String obtenerDeuda(String rut){
if(servicio != null){
try {
return servicio.obtenerDeuda(rut);
} catch (RemoteException ex) {
Logger.getLogger(AccesoDatos.class.getName()).log(Level.SEVERE, null, ex);
return null;
}

} else {
return null;
}
}

public String pagarDeuda(String rut, String monto){
if(servicio != null){
try {
return servicio.pagarDeuda(rut, monto);
} catch (RemoteException ex) {
Logger.getLogger(AccesoDatos.class.getName()).log(Level.SEVERE, null, ex);
return null;
}

} else {
return null;
}
}
}

Como pueden ver, para conectarnos utilizamos el método lookup de la clase Naming. Se recibe como parámetro, el host del servidor seguido del nombre del servicio.

Ya con ésto tenemos un objeto para instanciar los métodos remotos como si fuera de forma local.
El último paso importante es copiar el Stub al cliente. Recuerden que el Stub creado en el servidor pertenece al package org.remoto, el cuál, en el caso del servidor contiene 3 archivos .class (InterfazRemota, ServicioRemoto, ServicioRemoto_Stub) mientras que en el caso del cliente solo debe contener 2 (InterfazRemota, ServicioRemoto_Stub). No tiene ningún sentido que, en el cliente, exista ServicioRemoto.class ya que ésta clase implementa los métodos de la interfaz y utiliza recursos del servidor para proveer los servicios a los clientes.

De esa forma creamos un servicio con RMI en un servidor y creamos un cliente que consume ese servicio.

A continuación pueden ver el diagrama de clases del servidor:



y el diagrama de clases del cliente:



Qué ventajas existen ? aparte de las ya mencionadas (velocidad, agilidad, etc.) se tiene que es muchísimo mas rápido que web services. Sin embargo no es mejor que web services. Cada uno tiene su utilidad.

RMI sirve para desarrollar distemas distribuidos, pero solo de la misma tecnología RMI a java, Remoting a .NET, no permiten integración pero es rápido.


Web Services sirve para integrar sistemas distribuidos de distintas tecnologías, facilmente se pueden unir sistemas .NET con sistemas Java, pero es mas lento.


Acá les dejo unas capturas del sistema funcionando correctamente.



Como siempre, me pueden pedir el código dejándome sus correos y con gusto les enviaré el ejemplo de vuelta, es llegar y ejecutar. Además incluye el código fuente (desarrollado en NetBeans 6.0) y un informe sobre el trabajo.

saludos!!!!


EDITADO!!!

Subí el código a un servidor para que puedan hacer la descarga sin necesidad de enviarme sus correos (así no tienen que esperar a que yo lea el correo para rebotarlo a ustedes), si tienen problemas para la descarga seguimos con el método tradicional del correo electrónico.

Descarga

saludos !!!

Chat en Java usando Sockets en NetBeans 6.0

viernes, 7 de diciembre de 2007
Actualización:
Ver la siguiente entrada, un chat mejor que el aquí expuesto (desarrollado por mi también).

Hola, esta vez les traigo un chat que desarrollé en java. En la red hay varios, pero todos son demasiado simples y no muestran mucha funcionalidad, y la mayoria funcionan por consola. Todos los ejemplos que he encontrado solo envian Strings (cadenas de texto) por la red, pero que pasa si se quiere enviar objetos ? imagenes, colores, etc ?

El chat que desarrollé utiliza sockets, los cuales funcionan bajo una arquitectura cliente servidor, en donde se tiene un servidor y los clientes se conectan a éste y a través de él, se comunican entre si. La gracia del chat es que hace envio de objetos en vez de simples cadenas de texto, para ésto se utiliza la clase ObjectInputStream y ObjectOutputStream, los objetos de estas clases permiten escribir y leer objetos con los metodos writeObject y readObject, ambos reciben como parámetro un objeto. Cuál es el truco ? que todo lo que viaje por la red debe estar serializado, en el caso del chat se utiliza una clase Mensaje, la cual tiene un contenido (avatar, color de la fuente, mensaje en texto, etc) y cada parte del contenido debe estar serializado a su vez, si alguna cosa no está serializada, entonces la clase que lo contiene no podrá estarlo tampoco.

Para tener una visión completa del sistema, los invito a ver la arquitectura del chat:


Como pueden ver, el cliente consta de dos partes fundamentales, Cliente y Orquestador.

Cliente es la parte gráfica, la que el usuario vé y Orquestador es la parte que se conecta con el servidor y maneja la lógica (envía y recibe mensajes).

La parte servidor tiene un Servidor que recibe las conexiones y maneja una lista de clientes, la cual está en un objeto de la clase RecursoClientes, la cual utiliza el patrón de diseño Singleton (thread safe). Cuando un cliente se conecta, el Servidor recibe el nickname del Cliente y busca en la lista si ya existe, de ser así impide la conexión y avisa al Cliente (es una regla que implementé), en caso contrario la conexión se acepta y se delega el trabajo a un thread ManejadorCliente, el cual se encarga de recibir los mensajes del Cliente y se los pasa a los demás Clientes de la lista, además se encarga de quitarlo de la lista cuando éste se desconecta y avisa a los demás de la desconexión (entre otras cosas).

El problema con la arquitectura utilizada es que mientras mas clientes se conecten, mas recursos del servidor se utilizarán, ya que por cada cliente se crea un nuevo hilo (Thread). Una solución es NO usar Threads ni Sockets, sino usar SocketChannel de java.nio, pero es más difícil de utilizar (y entender), pero vale la pena ya que la baja en la utilización de recursos es altísima, además de una mejora sustancial en la eficiencia y rapidez de respuesta en el servidor. El cliente quedaría igual. En otra ocasión mostraré un caso utilizando SocketChannel.

Si les interesa pueden ver el diagrama de clases del cliente


y del servidor.


El servidor es bastante mas sencillo en el sentido de que son menos clases, pero hay que entender algunos conceptos que son mas difíciles de tragar, que son el patrón de diseño Singleton (thread safe) y la concurrencia con Threads. Por lo demás es bastante simple.

El cliente es mas complejo en el sentido que tiene mas clases, pero varias son solo clases para definir algunas cosas gráficas, nada complejo de entender. La clase mas importante es Orquestador, que es la que se encarga de toda la lógica y paso de mensajes hacia el cliente (a través de la interfaz OrquestadorInterface) y hacia el servidor (otros clientes).

Hay que tener en cuenta que lo que se envía por la red son objetos de tipo Mensaje, por lo tanto esa clase debe estar tanto en el cliente como en el servidor.

Acá pueden ver el diagrama de secuencia del servidor


y del cliente.

Con esos diagramas es fácil entender el paso de mensajes entre los objetos que conforman el software, tanto en la parte cliente como en el servidor. Por ejemplo se puede ver la secuencia de mensajes desde que un cliente se conecta hasta que se desconecta.

Una gracia de usar NetBeans 6.0 es que viene con UML integrado y soporta ingenieria inversa (o reversa) por lo que pueden hacer diagramas de cualquier tipo (soportado) desde el código fuente con un simple click derecho en el proyecto -> Reverse Engineer y listo. También pueden generar un nuevo proyecto UML y generar código a partir del diagrama de clases, es decir pueden utilizar UML hacia ambos lados, desde y hacia, lo cual encuentro sumamente genial y por sobre todo útil.

Por último decir que el código esta sumamente comentado y que, junto a los diagramas y explicaciones aquí dadas, se puede lograr un mayor entendimiento en menos tiempo, no tienen que adivinar lo que hice ni porqué, teniendo el código pueden entender muy bien como funciona todo y después modificarlo para sus necesidades (si, tienen mi permiso).

Código -> http://www.compartelo.cl/?q=36006

El hosting en el cual subí el código dura solo unos días, después de eso todo es eliminado, cuando eso suceda (lo sabrán porque no podrán descargarlo) dejenme su correo y se los envío por ese medio.

Funciona en cualquier Sistema Operativo que soporte java, yo lo compilé con java 1.6.0 update 03 de 64 bits en OpenSuse 10.3, pero como, ademas de los binarios, va el código fuente, lo pueden compilar para la arquitectura de su procesador y sistema operativo.

Aquí les dejo una capturas del chat funcionando.




saludos !!!

EDITADO!!!

Subí el código a un servidor para que puedan hacer la descarga sin necesidad de enviarme sus correos (así no tienen que esperar a que yo lea el correo para rebotarlo a ustedes), si tienen problemas para la descarga seguimos con el método tradicional del correo electrónico.

Descarga

EDITADO 19-08-2009

He creado un proyecto en Google Code así es que ahora pueden descargar el chat desde ahí sin tener que pedírmelo por correo o por el blog. Aún así agradeceré los comentarios que me puedan dejar en el blog.
El enlace es el siguiente: http://code.google.com/p/jtricahue/.

saludos !!!