domingo, 27 de noviembre de 2011

Curso Django Primera Parte

Hace tiempo que quiero hacer un pequeño curso-manual sobre django. En esta primera parte comenzaremos con el software base para llevarlo a la práctica. Espero que una vez terminado quede suficientemente claro y sobre todo, que sea de utilidad.

Software base:
- Debian 6.0.3
- Python
- Python setuptools
- gcc
- python-imaging
- Mysql
- python-mysqldb
- apache2
- Django-1.3.1.tar.gz -> https://www.djangoproject.com/download/
- digi604-django-smart-selects-1.0.4-0-ga8f9eb9.zip -> https://github.com/digi604/django-smart-selects
Hay un bug (https://github.com/digi604/django-smart-selects/issues/27) que se corrige con el parche que podemos encontrar en: https://github.com/GrAndSE/django-smart-selects/commit/7e2a8d572b3615cc39a3c9f9d60e1b60de06f46f
Habría que actualizar el fichero widgets.py en la carpeta smart_selects, tras esto instalar django-smart-selects.
- django-command-extensions -> svn checkout http://django-command-extensions.googlecode.com/svn/trunk/ django-command-extensions.
- Imaging-1.1.7.tar.gz -> http://www.pythonware.com/products/pil/
- python-pygraphviz

Con esto tendremos todo lo necesario para llevar a cabo el proyecto que vamos a crear en este manual, que será una aplicación de inventario de software.

Para su instalación sobre debian lo haremos con apt-get install, salvo aquellos que nos tengamos que descargar de su propia web, y que normalmente se instalan o bien compilando o bien con el instalador de python (python setup.py install).

Una vez tengamos instalado todo el software, crearemos nuestra bbdd y el usuario necesario, por lo tanto nos conectaremos a nuestra bbdd mysql y lanzaremos las siguientes sentencias:

mysql> create database programas;
mysql> grant all on programas.* to 'programas'@'localhost' identified by 'programas' with grant option;
mysql> grant all on programas.* to 'programas'@'localhost.localdomain' identified by 'programas' with grant option;

 Con esto creamos una bbdd llamada programa, con un usuario y pass programa.
 Ya tenemos todo listo para comenzar a crear nuestro primer proyecto en django.

Nos crearemos un directorio donde emplazar nuestro proyecto, por ejemplo:

mkdir /opt/djcode

Y nos situamos sobre este directorio. Una vez allí crearemos nuestro proyecto:

django-admin.py startproject misitio

Veamos qué ha ocurrido, si hacemos un listado del directorio /opt/djcode descubriremos un nuevo directorio llamado misitio, y si listamos éste a su vez, tendremos algo como esto:

__init__.py
manage.py
settings.py
urls.py

Estos ficheros compondrán el esqueleto de nuestro proyecto. Estos archivos son los siguientes:
__init__.py: Un archivo requerido para que Python trate a este directorio como un paquete (i.e. un grupo de módulos).
manage.py: Una utilidad de línea de comandos que te deja interactuar con este proyecto de Django de varias formas.
settings.py: Opciones/con guraciones para este proyecto de Django.
urls.py: La declaración de las URL para este proyecto de Django; una tabla de contenidos de tu sitio hecho con Django.

Veamos que hemos conseguido con esto, sin tocar ninguno de los fichero anteriores. Lanzaremos el servidor de desarrollos de django de la siguiente manera:

python manage.py runserver 192.168.48.129:8000

Lo que añadimos a continuación de runserver es la ip y puerto por el que queremos que se levante el servidor, y nos debe aparecer algo así:

Validating models...

0 errors found
Django version 1.3.1, using settings 'misitio.settings'
Development server is running at http://192.168.48.129:8000/
Quit the server with CONTROL-C.

Y si nos vamos a un navegador y probamos esta dirección, veremos un bonito mensaje de bienvenida.


Hasta aquí esta primera parte. En la segunda parte comenzaremos a ver cómo crear nuestros modelos y algo de la parte de administración de django, una verdadera joya.

Enlace a la 2ª partes: http://rafabono.blogspot.com.es/2012/06/curso-django-segunda-parte.html

viernes, 4 de noviembre de 2011

Road Salt Two

El nuevo disco de uno de mis grupos favoritos: Pain of Salvation. Obligada escucha del disco, genial, con una intro que te lleva a recordar el "Within You Without You" de The Beatles.

 Tracklist:

1. Road Salt Theme
2. Softly She Cries
3. Conditioned
4. Healing Now
5. To The Shoreline
6. Eleven
7. 1979
8. The Deeper Cut
9. Mortar Grind
10. Through The Distance
11. The Physics Of Gridlock
12. End Credits
 Página Oficial: http://www.painofsalvation.com/

viernes, 28 de octubre de 2011

Resetear pass de admin en Django

Para resetear la password de admin en Django en caso de olvido:

Entramos en la shell:

python manage.py shell

Aparecerá la siguiente interfaz de comandos:

Python 2.7.1+ (r271:86832, Apr 11 2011, 18:05:24)
[GCC 4.5.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>>



Una vez dentro ejecutaremos los siguientes comandos:

>>> from django.contrib.auth.models import User
>>> users = User.objects.all()
>>> u = users[0]
>>> u.set_password(‘la clave que queramos’)
>>> u.save()



Y listo, ya tenemos nuestra nueva password.

jueves, 27 de octubre de 2011

Error en ejecución de script en Alfresco

Hace tiempo publiqué una entrada con una serie de webscripts para facilitar ciertas tareas de administración de Alfresco: Pantalla de Administración para Alfresco.

Pues hace poco tuve que cambiar el tipo de autenticación en uno de los webscripts para permitir su uso a usuarios no adminstradores, para ellos cambié la autenticación a user. Fué entonces cuando surgió el problema. Para ciertos grupos, al lanzar el script me devolvía el siguiente mensaje:

500 Description: Un error en el servidor HTTP, el cual le ha impedido cumplir con la solicitud.

Message: 09246007631 Wrapped Exception (with status template): 09246007630 Failed to execute script '/org/alfresco/sample/prueba.post.js (in repository store workspace://SpacesStore/Empresa/Diccionario de datos/Web Scripts)': 09246007629 Acceso denegado. No tiene los permisos apropiados para realizar esta operación.

Exception: net.sf.acegisecurity.AccessDeniedException - Access Denied

Traceando el script me dí cuenta que el error se obtenía en el método: 
 lista = people.getMembers(grupoObtenido, false);

Comparando grupos en los que me daba el error encontré un usuario en común. Al quitar dicho usuario del grupo, el script volvía a correr adecuadamente. Por tanto el error lo estaba provocando dicho usuario.

Pasé a investigar las propiedades de dicho usuario a través del explorador de nodos y me percaté que el usuario poseía un permiso que el resto no tenía:
All ROLE_OWNER ALLOWED

Al tener el ROLE_OWNER, no permitía el acceso al resto de usuarios a obtener sus propiedades (razón por la que saltaba el error en el método getMembers).

Para reproducir el error, prueba a añadir ese permiso a un usuario de otro grupo. Esto lo podemos hacer a través de javascript:

usuario.setPermission("All", "ROLE_OWNER");

El efecto fue el esperado, al lanzar el script con este nuevo grupo saltó el error. Para corregirlo procedí a eliminar el permiso:

usuario.removePermission("All", "ROLE_OWNER");

Sin embargo, cuando volví a lanzar el script, seguía dando el error. Por algún motivo se había perdido un permiso que heredan todos los usuarios:

"Read", "GROUP_EVERYONE"

aunque aparentemente la herencia la tenía activada. Le otorgué este permiso:

usuario.setPermission("Read", "GROUP_EVERYONE");

y al lanzar nuevamente el script, ya corría adecuadamente.

Por algún motivo, al eliminar el permiso de ROLE_OWNER a través de javascript, la herencia de GROUP_EVERYONE se pierde. ¿Se trata de algún bug o estoy utilizando de forma incorrecta los permisos?.

De todos modos, está claro que para lanzar la mayoría de los scripts de administración, lo mejor es tener permisos de administrador, por lo que la solución que mejor se adapta a esto (y evitar este tipo de problemas) es configurar la autenticación con user, pero lanzar los scripts con la directiva runas:

<authentication runas="admin">user</authentication>
 
 Para poder utilizar esta directiva, los webscripts deben guardarse en el Java Class path.

domingo, 23 de octubre de 2011

Django y AJAX

Llevo unos meses utilizando el framework Django y me parece una maravilla. En la última aplicación que estoy realizando me he encontrado con un pequeño problema a la hora de utilizar en una plantilla tecnología AJAX. Concretamente el problema con el que me he topado ha sido para recoger unos datos mediante jQuery con un post. Tengo dos select combinados, de tal forma que al hacer la selección del primero, los datos del segundo se actualizan dependiendo del valor del primero.

Pues debido a la protección que trae Django para CSFR (Cross Site Request Forgery), la llamada post que hago para traerme los datos en JSON no es válida, ya que no estaba mandando el token válido para CSFR.

La solución, la propia documentación de Django te la da.

Básicamente se trata de meter este código en nuestro template:

$(document).ajaxSend(function(event, xhr, settings) {
    function getCookie(name) {
        var cookieValue = null;
        if (document.cookie && document.cookie != '') {
            var cookies = document.cookie.split(';');
            for (var i = 0; i < cookies.length; i++) {
                var cookie = jQuery.trim(cookies[i]);
                // Does this cookie string begin with the name we want?
                if (cookie.substring(0, name.length + 1) == (name + '=')) {
                    cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                    break;
                }
            }
        }
        return cookieValue;
    }
    function sameOrigin(url) {
        // url could be relative or scheme relative or absolute
        var host = document.location.host; // host + port
        var protocol = document.location.protocol;
        var sr_origin = '//' + host;
        var origin = protocol + sr_origin;
        // Allow absolute or scheme relative URLs to same origin
        return (url == origin || url.slice(0, origin.length + 1) == origin + '/') ||
            (url == sr_origin || url.slice(0, sr_origin.length + 1) == sr_origin + '/') ||
            // or any other URL that isn't scheme relative or absolute i.e relative.
            !(/^(\/\/|http:|https:).*/.test(url));
    }
    function safeMethod(method) {
        return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
    }

    if (!safeMethod(settings.type) && sameOrigin(settings.url)) {
        xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
    }
}); 


Tengo pendiente escribir un post con un pequeño manual de Django, donde explicar las nociones más básicas y crear un pequeño proyecto.

miércoles, 21 de septiembre de 2011

Control de procesos

Gracias a supervisor, podremos controlar de manera fácil y precisa los procesos que queramos. Presentaré cómo instalarlo sobre Ubuntu y un pequeño ejemplo de uso.

Lo primero que haremos será instalarlo desde el repositorio:

sudo apt-get install supervisor

Lo más probable es que nos de un error al no encontrar una dependencia: elementtree. Por tanto lo que haremos es desinstalarlo:

sudo apt-get remove supervisor

e instalar la dependencia:

sudo easy_install elementtree

Tras esto ya podremos instalar supervisor

sudo apt-get install supervisor

Bien, ya lo tenemos instalado y ejecutando, pero sin hacer nada útil, así que vamos a modificar el fichero de configuración que trae por defecto y que se encuentra en /etc/supervisor/supervisord.conf, añadiendo las siguientes entradas:

;activamos la interfaz web
[inet_http_server] ; inet (TCP) server disabled by default
port=*:9001 ; (ip_address:port specifier, *:port for all iface)
username=admin ; (default is no username (open server))
password=admin ; (default is no password (open server))

;añadimos un proceso a controlar
[program:cat]
command=/bin/cat ; the program (relative uses PATH, can take args)
process_name=%(program_name)s ; process_name expr (default %(program_name)s)
numprocs=1 ; number of processes copies to start (def 1)
directory=/tmp ; directory to cwd to before exec (def no cwd)


Existen muchas más opciones, así que lo mejor es que echéis un vistazo a la página oficial.

Una vez hechos los cambios, reiniciaremos supervisor:

sudo /etc/init.d/supervisor stop; /etc/init.d/supervisor start

Y podremos comprobar su funcionamiento accediendo a la url: http://localhost:9001

Desde aquí podemos parar/arrancar el proceso o ver el log (si escribe algún log y lo hemos configurado en superfisord.conf).

Ahora veamos si funciona realmente. Comprobaremos si nuestro proceso (un simple cat) está arrancado por supervisor:


Efectivamente, ahí está activo, con PID 3702 (al igual que vimos en la interfaz web).

Matemos el proceso con un kill -9 3702 y volvamos a hacer un ps:



Y ahí lo volvemos a tener, con un PID nuevo, como podremos comprobar desde la interfaz web.


Además de esto, supervisor tiene un modo consola muy útil que nos permitirá ver el estado de nuestros procesos y hacer con ellos prácticamente de todo. Tan sólo tenemos que entrar supervisorctl:


Si escribimos help, tendremos todas las opciones que nos ofrece.


Pues nada más, ahora a buscarle utilidad a esta gran herramienta.

lunes, 12 de septiembre de 2011

Controla el estado de la memoria en aplicaciones Java

En muchas ocasiones nos encontramos con aplicaciones que tienen leak de memoria, y se va consumiendo este recurso sin que sea liberado adecuadamente. La solución: corregir el código, que suele ser el principal origen del problema. Pero cuando eso no depende de ti y tienes que ofrecer disponibilidad en dicha aplicación, una posible solución es controlar el consumo que hace la aplicación de la memoria, y en función de esta, realizar un reinicio antes de su degradación.

Aquí dejo un script que puede resultar útil. Se basa en controlar como se consume la memoria en las zonas Eden y Old Generation. Al haber una fuga de memoria, la zona Old Generation no se liberará como debe, y la zona Eden se consumirá también, imposibilitando la creación de nuevos objetos.

El script lo que hace es controlar estas zonas y cuando se alcance cierto umbral durante cierto número de ciclos consecutivos, reiniciar la aplicación (evitamos que la aplicación se acabe degradando y que al final la tengamos que reiniciar a mano):



#!/bin/bash

#Se obtiene el PID del proceso java utilizando jps y filtrando por Bootstrap
PID=`jps | grep Bootstrap | cut -d " " -f 1`
DATE=`date +%d-%m-%y----%H:%M:%S`
#Log para registrar las acciones del propio script
LOG="/scripts/reinicio_mem.log"
#Fichero temporal
TMP="/tmp/valores.txt"
#Se lanza el jmap -heap para obtener los valores del Eden y Old Generation
su - tomcat -c "jmap -heap $PID | grep -A4 -e Eden -e Old | grep % | cut -d '%' -f 1" > $TMP
CONT=1
FICH_CONTADOR="/tmp/contador"
FICH_SEGUIDO="/tmp/seguido"
#Script para levantar tomcat
SCRIPT_ARRANQUE="/scripts/arranque_tomcat.sh"
#Script para parar tomcat
SCRIPT_PARADA="/scripts/parada_tomcat.sh"
#Script para el envío de correo
SCRIPT_CORREO="/scripts/envia_correo.py"

#Función para reiniciar tomcat
reinicio () {
    $SCRIPT_PARADA &
    wait
    $SCRIPT_ARRANQUE &
    wait
    $SCRIPT_CORREO &
    wait
    return 0
}


#Función para controlar el conteo de veces seguidas que supera el valor umbral. 
#Tras 5 veces seguidas se procede a reiniciar tomcat
contador () {
    if [ -e $FICH_SEGUIDO ]; then
        VALOR_CONTADOR=`cat $FICH_CONTADOR`
        VALOR_CONTADOR=`expr $VALOR_CONTADOR + 1`
        if [ $VALOR_CONTADOR -eq 5 ]; then
            rm -rf $FICH_SEGUIDO
            echo "Se alcanzan valores por encima del 99% durante 5 veces consecutivas. Se reinicia tomcat." >> $LOG
            echo 0 > $FICH_CONTADOR
            reinicio
        else
            echo $VALOR_CONTADOR > $FICH_CONTADOR
        fi
    else
        touch $FICH_SEGUIDO
        echo 1 > $FICH_CONTADOR
    fi
}

#Se asigna de forma dinámica el nombre de la variable y su valor
for i in $( cat $TMP | tail -2); do
    eval export "VAR_${CONT}=`echo $i`"
    CONT=2
done

echo $DATE >> $LOG
echo "Eden: " $VAR_1"%" >> $LOG
echo "Old: " $VAR_2"%" >> $LOG
let EDEN=`echo $VAR_1 | cut -d "." -f 1`
let OLD=`echo $VAR_2 | cut -d "." -f 1`
if [ $EDEN -ge 99 ] && [ $OLD -ge 99 ]; then
    echo "Eden y Old Generation por encima del 99%" >> $LOG
    contador
else
    echo "Eden y Old Generation por debajo del 99%" >> $LOG
    rm -rf $FICH_SEGUIDO
fi

Para el envío de correo, podemos utilizar un script como este:


 #!/usr/bin/env python
# -*- coding: iso-8859-15 -*-

import os, string,smtplib, socket
from email.MIMEBase import MIMEBase
from email.MIMEText import MIMEText
from email.Utils import COMMASPACE, formatdate
from email import Encoders

maquina = socket.gethostbyaddr(socket.gethostname())[0]

#Funcion para el envio de correo
def sendMail(to, subject, text, server="servidor.domino.es"):
    assert type(to)==list
    fro = maquina + "@dominio.es"

    COMMASPACE = ', '
    msg = MIMEText(text)
    msg['From'] = fro
    msg['To'] = COMMASPACE.join(to)
    msg['Date'] = formatdate(localtime=True)
    msg['Subject'] = subject


    smtp = smtplib.SMTP(server)
    smtp.sendmail(fro, to, msg.as_string() )
    smtp.close()


sendMail(
        ["destinatario@dominio.es"],
        "Reinicio " + maquina,"Se ha reiniciado la " + maquina + " tras 5 ciclos seguidos con valores por encima del umbral de uso de memoria."
        )

Obtener informacion de repositorios a través de los metadatos .git publicados por error

 A raiz de CTF realizado recientemente, me ha parecido interesante publicar este post sobre los errores de seguridad que se encuentran en mu...