HackIt! 2014 _ Level 1

24 de julio, a alguna hora de la noche cercana a las 22:00. Mis compañeros de DL me indican que marcan ha dado comienzo al HackIt de este año. Esta vez no podré acudir a la presentación y me tendré que aguantar hasta el viernes 25 a eso de las 18:00, así que estaré un día ayudando como pueda a través del móvil :-O Cuando el 25 llego por fin a mi puesto, abro el portátil y me sumerjo en el reto… del que no me despegaré hasta el domingo 27 a las 14:00 de la tarde… o para ser más exactos, a las 14:01, como podréis comprobar en los siguientes posts.

A decir verdad, los retos comenzaron mucho antes… en la lista de correo del HackIt, donde, como todos los años marcan nos pidió colaboración en la elaboración de las pruebas. Llevaba dándole vueltas a la idea de crear un nivel donde hiciera falta explotar la vulnerabilidad HeartBleed, y este año, a diferencia de otros, conseguí pasar de la idea a la realidad 🙂 Como de costumbre, marcan hizo que esa realidad fuera aún más enrevesada (¿a quién demonios se le ocurrió si no, desplegar el level en una máquina con arquitectura Big Endian?).

Coincidimos con la gente de amn3s1a, w0pr, NavarParty y TimoSoft y nos «peleamos» por el segundo y tercer puesto (el primero, it goes without saying, está reservado para w0pr, juegan en otra liga). También estuvo Willix Team, aunque no pudimos hablar con ellos pues no les vimos cerca de la Raspberry Pi (enseguida sabréis la razón de que aparezca aquí este dispositivo).

En fin, buenos recuerdos, muchas cosas que aprendimos por el camino y muchas más cosas que apunté para profundizar y aprender hasta el HackIt de 2015 😉 En esta serie de posts, siguiendo la recomendación del propio admin de la prueba, pasaré a comentar nuestra forma de superar algunos de los niveles. No todos… la prueba de reversing en ARM se nos atragantó (aquí agradecería que la gente de W0pr – o tal vez NavarParty, no recuerdo bien si consiguieron superarla – nos iluminara con un write-up); y con respecto a la última, la «marcanada del año» (sic), llegamos sobre la campana (un minuto más allá) y no pudimos ni intentarlo. También se agradecería un write-up, especialmente a @abeaumont, del que sabemos que estuvo muuuuchas horas pegado a la pantalla hasta solucionarlo 😉

El primer nivel, Lienzo Digital, empieza diciendo que podrás superarlo rápidamente, a no ser que uses Internet Explorer. Analizando el código fuente, nos encontramos con un script en JS, que tras pasarlo por un beautifier, nos dice algo como:

$(document).ready(function() {
    var a = new Image();
    a.addEventListener("load", function() {
        $("#password").keyup(function(b) {
            var c = $("#password").val();
            if (27 != c.length) {
                $("#password").css({
                    "background-color": "#f88"
                });
                return;
            }
            var d = 0;
            var e = document.createElement("canvas");
            e.width = 9;
            e.height = 1;
            var f = e.getContext("2d");
            f.globalCompositeOperation = "difference";
            var g = f.createImageData(9, 1);
            for (var h = 0; h < 36; h++) g.data[h] = 255;
            for (var h = 0; h < c.length; h++) g.data[h + Math.floor(h / 3)] = Math.min(c.charCodeAt(h), 255);
            f.putImageData(g, 0, 0);
            for (var h = 0; h < 9; h++) f.drawImage(a, 0, -h);
            var g = f.getImageData(0, 0, 9, 1);
            for (var h = 0; h < 27; h++) d |= g.data[h + Math.floor(h / 3)];
            $("#password").css({
                "background-color": d ? "#f88" : "#8f8"
            });
        });
    });
    a.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAIAAABv85FHAAABB0lEQVR4nAH8AAP/APXX7L74zhsb6Bni6gjRu84PCwPnFRrD/iQJEADp+fop4w7/ClXcP1SiE/cmvMOp+MHm/T7ppegA1O7CJP/IGQvj+xTHyPU3uRbJ6gA34E3f4dNfAOsR5vkP0TXLIO7E3uYm1Auzux4bx+fsD9bN9QD3IbM6yxDkNLjgussk5SUKCxXK1dMIHKPVMf8A2REc1MHrOtrT962///YfuB++E8fdDzAQoanoAOKODsDtBa31xuj6IizvxQYV3AIv4+wkxgy1vgAJFqLRFxD7HQCh7MwE3Aq0qwzg6yr69d28AtUBnV1+0/HzLiLX19o25jfnBP/hIDkMDBUX3YAUC0mGddt48B4AAAAASUVORK5CYII=";
});

Lo primero que vemos es que la longitud del pass debe ser 27 (en otro caso, el css que se aplica es de color rojizo #f88). Lo siguiente es que el level crea un elemento canvas (lienzo digital…) con el que empieza a jugar. Dentro del canvas (‘f’) tenemos representado un array de 9×1 pixels en una variable de nombre ‘g’. Cada pixel, a su vez, viene representado en RGBA (por tanto, con 4 bytes por pixel, necesitamos 36 bytes para gestionar dicho array). Por otro lado, en la variable de nombre ‘a’ tenemos una extraña imagen usando un Data URI Scheme (RFC 2397). Si copias y pegas esa imagen como una URL normal en el navegador, podrás visualizarla. La idea para pasar este nivel es que con los 27 caracteres del password (su código ASCII) rellenamos los datos del array g. ¿Por qué 27 y no 36? Porque en el bucle vemos que cada 3 bytes, nos saltamos uno (g.data[h + Math.floor(h/3)] = código ascii).

A continuación hay tres líneas que vuelcan el valor del array g (construido con los valores ASCII del pass, recuerda) en el canvas ‘f’ y calculan una diferencia de ‘f’ con ‘a’ (el f.globalCompositeOperation = ‘difference’ entra en juego ahora), dejando el resultado en ‘g’.

El último «meneo» aplica un OR a los datos de ‘g’ y deja el resultado en ‘d’.

d = 0;
for (var h = 0; h < 27; h++) d |= g.data[h + Math.floor(h / 3)];

El objetivo es conseguir satisfacer que ‘d’ valga 0 al salir del bucle. Si lo conseguimos (para ello, todos los datos de g.data deben ser 0), el valor del background del campo pass será verde: («background-color»: d ? «#f88» : «#8f8»), y por tanto, sabremos que el password es correcto.

Abramos la consola de Chrome y comencemos a jugar. Pegamos el siguiente trozo de código:

var a = new Image();
a.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAIAAABv85FHAAABB0lEQVR4nAH8AAP/APXX7L74zhsb6Bni6gjRu84PCwPnFRrD/iQJEADp+fop4w7/ClXcP1SiE/cmvMOp+MHm/T7ppegA1O7CJP/IGQvj+xTHyPU3uRbJ6gA34E3f4dNfAOsR5vkP0TXLIO7E3uYm1Auzux4bx+fsD9bN9QD3IbM6yxDkNLjgussk5SUKCxXK1dMIHKPVMf8A2REc1MHrOtrT962///YfuB++E8fdDzAQoanoAOKODsDtBa31xuj6IizvxQYV3AIv4+wkxgy1vgAJFqLRFxD7HQCh7MwE3Aq0qwzg6yr69d28AtUBnV1+0/HzLiLX19o25jfnBP/hIDkMDBUX3YAUC0mGddt48B4AAAAASUVORK5CYII=";
var d = 0;
var e = document.createElement("canvas");
e.width = 9;
e.height = 1;
var f = e.getContext("2d");
f.globalCompositeOperation = "difference";
var g = f.createImageData(9, 1);
for (var h = 0; h < 36; h++) g.data[h] = 255;

Inspeccionando un poco el entorno, vemos lo siguiente:

// creamos un pass de 27 'a's
> var c = Array(27).join("a")
> g.data
[255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,255, 255, 255, 255, 255, 255, 255, 255, 255]
> for (var h = 0; h < c.length; h++) g.data[h + Math.floor(h / 3)] = Math.min(c.charCodeAt(h), 255);
> f.putImageData(g, 0, 0);
> f.getImageData(0,0,9,1).data
[97, 97, 97, 255, 97, 97, 97, 255, 97, 97, 97, 255, 97, 97, 97, 255, 97, 97, 97, 255, 97, 97, 97, 255, 97, 97, 97, 255, 97, 97, 97, 255, 97, 97, 255, 255]

¿Y qué datos tenemos en la imagen misteriosa?

var b = new Image()
var myCanvas = document.createElement("canvas")
var ctx = myCanvas.getContext("2d")
 b.onload = function(){ ctx.drawImage(b,0,0); } ; b.src = a.src;
 // primera fila de la imagen misteriosa
 
 ctx.getImageData(0,0,9,1).data
[245, 215, 236, 255, 190, 248, 206, 255, 27, 27, 232, 255, 25, 226, 234, 255, 8, 209, 187, 255, 206, 15, 11, 255, 3, 231, 21, 255, 26, 195, 254, 255, 36, 9, 16, 255]
 // segunda fila de la imagen misteriosa
 ctx.getImageData(0,1,9,1).data
[233, 249, 250, 255, 41, 227, 14, 255, 255, 10, 85, 255, 220, 63, 84, 255, 162, 19, 247, 255, 38, 188, 195, 255, 169, 248, 193, 255, 230, 253, 62, 255, 233, 165, 232, 255]

OK! Ahora sólo nos queda entender esto:

for (var h = 0; h < 9; h++) f.drawImage(a, 0, -h);

La primera iteración del bucle es sencilla: f.drawImage(a,0,0) . Lo que estamos haciendo es copiar en el canvas (desde la posición 0,0 del canvas) los bytes del array ‘a’. Pero como hemos definido que el globalCompositeOperation del canvas es «difference», realmente lo que hacemos no es machacar lo que ya hubiera en el canvas (la ristra de 97, 97, 97…) sino que se calcula la diferencia. Es decir, estamos calculando esta operación: abs( [97, 97, 97, 255, …, 97, 97, 255, 255] – [245, 215, 236, 255, …, 36, 9, 16, 255]), obteniendo como resultado: [148, 118, 139, 255, …, 61, 88, 239, 255].

El modo difference es uno de los posibles modos «composite» del canvas HTML5. Podéis investigar al respecto en este ejemplo de CodePen.io.

Este es un ejemplo de composite «normal» (fíjate en las zonas de solapamiento en los colores magenta, cyan y amarillo)

normal

y este otro un ejemplo de composite «difference».

difference

La segunda iteración del bucle tiene una pequeña complejidad: f.drawImage(a,0,-1) . ¿Qué es ese -1? Que la imagen misteriosa se solapa sobre el canvas, pero saltando una fila, es decir, estaremos haciendo:
abs ([148, 118, 139, 255, …, 61, 88, 239, 255] – [233, 249, 250, 255, …, 233, 165, 232, 255]) obteniendo [85, 131, 111, 255, …, 172, 77, 7, 255]

Y seguimos igual para f.drawImage(a,0,-2) … f.drawImage(a,0,-8). Recordemos que en la última iteración debemos obtener [0,0,0,255,0,0,0,255,…,0,0,0,255]. Así que, deshaciendo el entuerto tenemos que para la primera letra del pass hay que resolver:

|||||||245 (pos 0,0 de la imagen) – Letra del pass| – 233 (pos 0,1 de la imagen)| – …. – pos(0,8 de la imagen)| = 0

Es decir:

Math.abs(Math.abs(Math.abs(Math.abs(Math.abs(Math.abs(Math.abs(Math.abs(Math.abs(x – 245) – 233) – 212) – 235) – 247) – 217) – 226) – 9) – 157) = 0

Para la segunda letra del pass, de forma equivalente, hay que resolver:

|||||||215 (pos 1,0 de la imagen) – Letra del pass| – 249 (pos 1,1 de la imagen)| – …. – pos(1,8 de la imagen)| = 0

etc.

Resolviendo la ecuación para todas las letras del pass:

// preparar la imagen en el contexto del canvas
var b = new Image()
var myCanvas = document.createElement("canvas")
var ctx = myCanvas.getContext("2d")
b.onload = function(){ ctx.drawImage(b,0,0); } ; 
b.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAIAAABv85FHAAABB0lEQVR4nAH8AAP/APXX7L74zhsb6Bni6gjRu84PCwPnFRrD/iQJEADp+fop4w7/ClXcP1SiE/cmvMOp+MHm/T7ppegA1O7CJP/IGQvj+xTHyPU3uRbJ6gA34E3f4dNfAOsR5vkP0TXLIO7E3uYm1Auzux4bx+fsD9bN9QD3IbM6yxDkNLjgussk5SUKCxXK1dMIHKPVMf8A2REc1MHrOtrT962///YfuB++E8fdDzAQoanoAOKODsDtBa31xuj6IizvxQYV3AIv4+wkxgy1vgAJFqLRFxD7HQCh7MwE3Aq0qwzg6yr69d28AtUBnV1+0/HzLiLX19o25jfnBP/hIDkMDBUX3YAUC0mGddt48B4AAAAASUVORK5CYII=";
 
// resolver la ecuación
pass = ''
for (ind = 0; ind < 36; ind++) {
  for (letra=32;letra<127;letra++) {
   c = letra; 
   for(var i=0; i < 9; i++) {
       c = Math.abs(ctx.getImageData(0,i,9,1).data[ind] - c)
   } 
   if(c == 0)
      pass += String.fromCharCode(letra);
   }
}
console.log(pass)

Obtenemos lo que buscábamos 😉

Receta rápida para optimizar MySQL

highperformancemysqlSiguiendo con el post anterior (recordemos, tengo un servidor muy humilde en recursos que quiero aprovechar al máximo), le toca el turno al servidor MySQL.

Tengo un libro (High Performance MySQL, de la editorial O’Reilly) que -entre otras cosas- se dedica a explicar cómo obtener el mejor rendimiento de un servidor MySQL, así que en este post no voy a hacer más que abrir el melón 🙂

La cuestión es que, antes de entrar a configurar los cientos de parámetros del fichero de configuración my.cnf, quería ir a tocar los interruptores «evidentes». ¿Hay alguna forma de encontrar fallos de configuración obvios en MySQL para hacer que este sistema gestor rinda mejor? («rendir mejor» puede entenderse de varias formas… yo me refiero al sentido de que siga ofreciendo servicio pero que consuma menos RAM).

Pues sí… hay una pequeña aplicación, llamada mysqltuner.pl, que permite «tunear» el fichero my.cnf de forma muy sencilla. Realmente lo que hace es darte pistas y consejos sobre qué «interruptores» tocar para que el servidor rinda mejor.

Aprovechando un domain hack, el autor de la aplicación ha generado un dominio propio con el mismo nombre que la aplicación en Perl: http://mysqltuner.pl. Así que, para descargar y hacer ejecutable esta joyita, basta con hacer:

wget -O mysqltuner.pl http://mysqltuner.pl
chmod a+x mysqltuner.pl
sudo ./mysqltuner.pl

Si ejecutamos con permisos de sudoer, ni siquiera tenemos que configurar las credenciales de mysql, porque como la aplicación indica en el log:

[OK] Logged in using credentials from debian maintenance account.

Qué cosas… no sabía que hubiera una cuenta de mantenimiento debian con la que hacer… eso, mantenimiento de mysql 🙂 Entre otras cosas, lo primero que me dice mysqltuner es:

-------- Storage Engine Statistics -------------------------------------------
[--] Status: +ARCHIVE +BLACKHOLE +CSV -FEDERATED +InnoDB +MRG_MYISAM 
[--] Data in MyISAM tables: 9M (Tables: 14)
[--] Data in PERFORMANCE_SCHEMA tables: 0B (Tables: 17)
[!!] InnoDB is enabled but isn't being used
[!!] Total fragmented tables: 5
...
-------- Recommendations -----------------------------------------------------
General recommendations:
    Add skip-innodb to MySQL configuration to disable InnoDB
    Run OPTIMIZE TABLE to defragment tables for better performance
    MySQL started within last 24 hours - recommendations may be inaccurate
    Reduce your overall MySQL memory footprint for system stability
    Enable the slow query log to troubleshoot bad queries

Vamos, que el módulo InnoDB lo tengo activado (consumiendo memoria) y no lo uso. Sabiendo que mi server si algo tiene que cuidar es la RAM… vamos a hacerle caso… También me dice que tengo tablas fragmentadas que convendría arreglar. Apuntado. Lo primero, antes de pifiarla (desactivando InnoDB cuando por defecto MySQL crea bases de datos InnoDB, por ejemplo), vamos a comprobar…

mysql> use information_schema ;
Database changed
mysql> select * from ENGINES;
+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
| ENGINE             | SUPPORT | COMMENT                                                        | TRANSACTIONS | XA   | SAVEPOINTS |
+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
| FEDERATED          | NO      | Federated MySQL storage engine                                 | NULL         | NULL | NULL       |
| CSV                | YES     | CSV storage engine                                             | NO           | NO   | NO         |
| MRG_MYISAM         | YES     | Collection of identical MyISAM tables                          | NO           | NO   | NO         |
| BLACKHOLE          | YES     | /dev/null storage engine (anything you write to it disappears) | NO           | NO   | NO         |
| MyISAM             | YES     | MyISAM storage engine                                          | NO           | NO   | NO         |
| MEMORY             | YES     | Hash based, stored in memory, useful for temporary tables      | NO           | NO   | NO         |
| ARCHIVE            | YES     | Archive storage engine                                         | NO           | NO   | NO         |
| InnoDB             | DEFAULT | Supports transactions, row-level locking, and foreign keys     | YES          | YES  | YES        |
| PERFORMANCE_SCHEMA | YES     | Performance Schema                                             | NO           | NO   | NO         |
+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
9 rows in set (0.00 sec)

Je… Murphy nunca falla. Vamos a arreglar eso (quiero dejar MyISAM por defecto antes de desactivar el soporte InnoDB en MySQL). En el fichero /etc/mysql/my.cnf añado lo siguiente:

default-storage-engine=myisam 
skip_innodb

Y listo. Vamos ahora a optimizar tablas…

mysqlcheck -u root -p -o --all-databases

Pasamos de nuevo mysqltuner.pl:

-------- Storage Engine Statistics -------------------------------------------
[--] Status: +ARCHIVE +BLACKHOLE +CSV -FEDERATED -InnoDB +MRG_MYISAM 
[--] Data in MyISAM tables: 9M (Tables: 14)
[--] Data in PERFORMANCE_SCHEMA tables: 0B (Tables: 17)
[OK] Total fragmented tables: 0

y ¡premio!

Dejo un ejercicio para el lector 🙂

-------- Performance Metrics -------------------------------------------------
[--] Up for: 4h 9m 7s (43K q [2.891 qps], 1K conn, TX: 152M, RX: 5M)
[--] Reads / Writes: 82% / 18%
[--] Total buffers: 192.0M global + 2.7M per thread (151 max threads)
[!!] Maximum possible memory usage: 597.8M (121% of installed RAM)
-------- Recommendations -----------------------------------------------------
General recommendations:
    ...
    Reduce your overall MySQL memory footprint for system stability
    ...

Eso de que el máximo de RAM que puede consumir MySQL es del 121% de la RAM instalada no me ha gustado (y a mysqltuner.pl tampoco). ¿Qué hacer para corregirlo? Lo veremos en otro post…

Desactivando módulos innecesarios en Apache

Tengo un servidor Apache montado en una máquina física muy justita de recursos (especialmente de memoria RAM). El servidor Apache que viene por defecto tendría que cambiarlo por nginx. Por lo que dicen en los mentideros de Internet parece que consume menos recursos que Apache… mientras tomo la decisión de migrar, he optado por recortar módulos de Apache que no necesito. Puedes ver la lista completa de módulos Apache lanzados así:

apachectl -M

En concreto, para montar un WordPress sencillito, he desactivado los siguientes, por innecesarios:

a2dismod status
a2dismod negotiation
a2dismod env
a2dismod autoindex
a2dismod authn_file
a2dismod authz_file
a2dismod authz_user
a2dismod auth_basic
a2dismod authn_core

Ahora, la lista queda así (filtrando los módulos básicos, que no se pueden deshabilitar):

apachectl -M | grep shared
 
access_compat_module (shared)
alias_module (shared)
authz_core_module (shared)
authz_host_module (shared)
deflate_module (shared)
dir_module (shared)
filter_module (shared)
mime_module (shared)
mpm_prefork_module (shared)
php5_module (shared)
rewrite_module (shared)
setenvif_module (shared)

Si partes de cero y quieres ver qué módulos puedes desactivar, te recomiendo el siguiente procedimiento:

1) teclea a2dismod y a continuación pulsa TAB dos veces. Bash te dará los nombres de los módulos desactivables.

2) deshabilita uno a uno los módulos. Por ejemplo,

a2dismod env

y a continuación, comprueba que tu configuración Apache no depende de ese módulo usando

apachectl configtest

Con eso no es que te asegures al 100% de que no la has pifiado, pero te será de gran ayuda hacerlo, porque detecta pifias antes de relanzar Apache…

3) Relanzar Apache

service apache2 restart

Si quieres saber más al respecto, te recomiendo que leas este hilo de discusión en stackoverflow.com y que antes de deshabilitar módulos alegremente, le eches un vistazo a lo que hace cada uno de ellos en la documentación de Apache. Por ejemplo, si quisieras ver qué hace y para qué sirve el módulo env, ésta sería la página del manual de Apache.

Introducción a Open edX (III). Devstack

Como dijimos en el anterior post, Devstack es una instancia Vagrant diseñada para facilitar la vida a los desarrolladores de Open edX. Diferenciaremos Devstack de Fullstack en que ésta última es una instancia Vagrant diseñada para tener todos los servicios de edX en un único servidor de producción.

Además, Devstack simplifica algunas características de Fullstack para hacerlo más ligero (y simplificar así el desarrollo en local). Por ejemplo, en lugar de usar el servidor HTTP nginx y gunicorn como hace Fullstack, Devstack usa el runserver de Django, mucho más ligerito.

Devstack incluye los componentes LMS, CMS (Studio), los foros y ORA (Open ResponseAssessor). Este último componente, no obstante, no está configurado en Devstack (y requiere mucha máquina para su ejecución, por lo que no lo recomiendo en modo desarrollo).

Antes de empezar con la instalación, es necesario asegurarse de que tenemos una versión modernita de VirtualBox (4.3.10 o superior) y Vagrant (1.5.3) o superior. En Ubuntu 13.10 (donde he probado este proceso de instalación) tuve que descargar desde la web de cada herramienta la última versión (las versiones que ofrecían los repos APT no eran lo suficientemente modernas). En concreto, he hecho las pruebas con VirtualBox 4.3.12 y Vagrant 1.6.2.

Instalando Devstack

Lo primero, necesitaremos instalar soporte NFS. ¿Para qué? para que nuestra máquina local exporte las carpetas de desarrollo -donde tendremos parte del código fuente de edX- a la instancia vagrant (esto se hace con la ayuda de las VirtualBox Guest Additions, pero por ahora, olvídate de este detalle, simplemente instala el soporte NFS tal y como indico):

sudo apt-get install nfs-common nfs-kernel-server

Vamos a crear ahora un directorio donde trabajar con Devstack:

cd /opt/
mkdir devstack
cd devstack

Descargamos ahora el fichero Vagrant necesario para comenzar la instalación:

curl -L https://raw.github.com/edx/configuration/master/vagrant/release/devstack/Vagrantfile &gt; Vagrantfile

Nota: resumiendo muy mucho, podemos ver el fichero Vagrantfile como una receta sobre cómo montar una máquina virtual desde cero: cuál es la imagen base, qué puertos abrir en dicha máquina virtual, cuánta memoria asociarle, qué carpetas compartir, etc.

Antes de lanzar vagrant, instalaremos un plugin que no va a permitir tener siempre actualizadas las VirtualBox Guest Additions (recordemos, un conjunto de herramientas de VirtualBox que entre otras cosas nos permitirán compartir carpetas entre la máquina física (host) y la máquina virtual (guest).

vagrant plugin install vagrant-vbguest

Procedemos a lanzar vagrant:

vagrant up

Ojo, la primera vez que lancemos este comando Vagrant procederá a descargar la máquina base sobre la que instalar edX. Esta máquina base ocupa 4GB… así que no lo hagas desde tu conexión móvil 😉 A partir de aquí, aunque destruyas la máquina virtual, Vagrant no tendrá que descargarse de nuevo la imagen base.
Durante la instalación, cuando vaya a compartir las carpetas vía NFS, vagrant te pedirá el password de tu usuario con permisos sudo.

vagrant@precise64: ~_098

Si te quieres ahorrar todo este proceso, puedes descargar directamente una máquina virtual edX preconfigurada con Vagrant vía Torrent.

Tras ello, podrás lanzar la máquina usando el siguiente comando:

vagrant box add box-name path-to-box-file

Conectar con Devstack vía SSH

Ok, ya está instalado Devstack. ¿Ahora qué? Lo primero será lanzar el LMS y el CMS. Para ello, nos conectaremos vía ssh con la máquina virtual que acabamos de lanzar. ¿Cómo? Usando vagrant:

vagrant ssh

¿Fácil, no? Pues sí… vagrant es una maravilla 🙂

Ahora tendremos que cargar algunas variables de entorno. El usuario edxapp las tiene configuradas en sus
ficheros de inicio de sesión, así que vamos a decirle a Devstack que queremos trabajar como si fuéramos edxapp:

sudo su edxapp

Además, este usuario tiene su HOME en /edx/app/edxapp/edx-platform (la carpeta raíz de la plataforma edX)
por lo que estaremos ahí situados al lanzar el comando anterior.

Bien, sin más dilación, hagamos una prueba rápida. Vamos a lanzar el LMS con el comando paver:

(edx-platform)$  paver devstack lms

(la primera vez, le va a costar, porque estará actualizando/descargando los requerimientos para lanzar el LMS y compilando los recursos estáticos (css, js y demás). Si ya lanzaste alguna vez el LMS y no quieres actualizar recursos/requerimientos, puedes hacer uso de la opción –fast  (paver devstack –fast lms).

¡Listo! Abrimos un navegador (en la máquina física) y tecleamos: http://localhost:8000/. Deberíamos ver el LMS tal y como indico en la imagen adjunta.

| edX - Google Chrome_099

 

 

Para lanzar el CMS (Studio), procederemos igual:

(edx-platform)$  paver devstack studio

El CMS se abre en el puerto 8001, así que, abrimos un navegador (en la máquina física) y tecleamos: http://localhost:8001/. Si todo va bien, veremos algo como lo de la figura 2.

Welcome | edX Studio - Google Chrome_100

Devstack tiene varias cuentas de usuario creadas por defecto. Para nuestras pruebas, puedes conectarte como profesor usando el login staff@example.com (pass: edx) y como alumno usando el login verified@example.com (pass:edx).

En el siguiente post veremos cómo lanzar los foros y cómo configurar edX para poder crear nuevas cuentas de usuario.

Introducción a Open edX (II)

La arquitectura de la plataforma edX cuenta con varios componentes, tal y como puede apreciarse en la figura adjunta. Iremos desgranando todos ellos en diferentes posts.

arquitectura_edx

Hoy empezaremos a tratar los dos más importantes: el LMS (Learning Management System) y el CMS (Content Management System), éste último también conocido como edX Studio. El primero (LMS) forma la aplicación web y frontend que ven y usan todos los alumnos del sistema edX. El segundo (CMS) es la aplicación web que usan los profesores para crear y organizar los contenidos que irán al LMS.

Para empezar a probar tanto el LMS como el CMS de Open-edX hay varias posibles vías de instalación: 1)  instalar desde cero, en la máquina local – o remota, pero en el sistema de archivos físico de la máquina – , descargando el código fuente y las dependencias.2) Instalar en la máquina local o remota pero, en lugar de hacerlo sobre la propia máquina física, hacerlo sobre una máquina virtual (VirtualBox) usando Vagrant para simplificar/automatizar los pasos. 3) Instalar usando una máquina AMI preparada para la nube Amazon (AWS).

De estas tres formas, la más fácil es la última, se puede hacer en 10 minutos si ya dispones de una cuenta AWS. Lógicamente, ojo con los costes. AWS es una solución muy potente, versátil y con multitud de herramientas que facilitan el despliegue de aplicaciones en esa nube (IAAS, Infrastructure As A Service) , pero también es una de las plataformas en la nube más caras. ¿Alternativa? Usar DigitalOcean. Mucho más barato… pero no dispone de tantas facilidades y servicios como AWS. Hay que hacerlo todo de forma más artesanal…

Por otra parte, la instalación de edX puede hacerse como entorno de desarrollo (pruebas) y como entorno de producción (explotación). La versión de desarrollo es recomendable instalarla en local usando Vagrant (modo 2 de los tres citados). Realmente es una versión de edX limitada, no está configurada para hacer uso del módulo ORA (Open Response Assessor), permite entre otras cosas la evaluación automática de ejercicios, así como la evaluación entre pares), por ejemplo. Pero viene muy bien para probar por primera vez la plataforma y/o para desarrollar nuevas funcionalidades.

La versión de desarrollo se recomienda instalarla sobre una máquina virtual Ubuntu. Las pruebas realizadas se han hecho sobre un PC con 4 GB de RAM y una CPU Intel Core 2 Duo CPU a 2.40GHz (aunque para  desarrollo podría valer una máquina con menos recursos, es recomendable que tenga esa  capacidad en memoria debido al alto consumo de RAM de la máquina virtual). Se recomienda disponer al menos de 20GB de espacio en disco (sólo la máquina virtual, sin configurar, requiere de 2GB).

La versión de explotación requiere de una máquina online, con sistema operativo Ubuntu, similar a una m1.large en la nube Amazon (arquitectura 64 bits, doble procesador Intel Xeon, 7.5 GB RAM, con unos 50 GB de espacio en disco). Puede instalarse directamente sobre el sistema de archivos de la máquina física o bien crear internamente una máquina virtual e instalar la versión de producción sobre dicha máquina virtual.

En el siguiente post veremos en detalle cómo instalar una versión edX de desarrollo en la máquina local usando VirtualBox y Vagrant.