Category Archives: Apache

Publicar una Aplicación de DJango en un Contenedor Docker

Podemos estar tentados de pasar a producción el contenedor Docker con el runserver que proporciona el manager.py de DJango. Pero esto es un gravísimo error de seguridad. En la siguiente entrada explicaremos como crear el contenedor Docker con el módulo wsgi de Apache.

En la siguiente entrada veremos lo sencillo que es construir nuestra imagen para Docker de nuestra App de DJango con el Dockerfile que nos proporciona Graham Dumpleton.

Lo único que necesitamos es el código DJango de la aplicación (obviamente) y el fichero requirements.txt que debe estar en la raiz donde vamos a colocar nuestro Dockerfile.

Pongamos que nuestro requirements.txt tiene este aspecto:

numpy==1.6.2
Django==1.4.2
django-tastypie==0.9.14
pyes==0.19.1

Lo siguiente que necesitamos conocer es el dónde colocar los estáticos tanto la URL como la ubicación física en el directorio.

Lo típico es poner los estáticos bajo la URL /static/ y en el disco en el directorio static de nuestra aplicación. Debemos conocer estos datos para pasarlo al comando con el que arrancar la imagen.

Bien, como decíamos, partimos de la imagen siguiente

FROM grahamdumpleton/mod-wsgi-docker:python-2.7-onbuild

Usaremos onbuild porque tiene dos disparadores interesantes que nos simplifican muchísimo la vida. El primero copia el contenido del directorio actual a /app y el segundo ejecuta

pip install -r requirements.txt

Con el contenido de nuestro fichero de requirements.

La única línea que tenemos que añadir a nuestro Dockerfile sería la siguiente:

CMD ["--url-alias", "/static", "awesomeapp/static", "awesomeapp/wsgi.py"]

Y la construimos

# docker build -t awesomeapp-wsgi .

Ya podemos ponerla en producción sin riesgo.

# docker run -d -p 80:80 --name awesomeapp-pro awesomeapp-wsgi

Referencias:

  1. http://blog.dscpl.com.au/2014/12/hosting-python-wsgi-applications-using.html
  2. https://hub.docker.com/r/grahamdumpleton/mod-wsgi-docker/
  3. https://github.com/GrahamDumpleton/mod_wsgi-docker

Apache AuthBasic con ActiveDirectory

En esta entrada veremos como configurar el módulo AuthBasic de Apache para que autentique contra LDAP de ActiveDirectory.

Ya tenemos instalado Apache con el módulo habilitado y tenemos que añadir el siguiente bloque dentro de la definición del VirtualHost.

AuthType Basic
AuthBasicProvider ldap
AuthName "Introduzca sus credenciales"
AuthzLDAPAuthoritative off # Aquí indicamos que podemos tener otras formas de autenticación
AuthLDAPBindDN "Usuario de busqueda en LDAP"
AuthLDAPBindPassword "Contraseña para el usuario de búsqueda en LDAP"
AuthLDAPURL "ldap://SERVER:3268/Cadena de conexión al árbol LDAP" NONE # El NONE indica que no hay cifrado en la conexión, podemos usar TLS, SSL, etc.
Require valid-user

Esta configuración seria suficiente para que los usuarios del Dominio se autentiquen ante Apache con sus credenciales.

Shared Web Hosting con Varnish

Hemos decidido montar un OpenVZ sobre un servidor físico para ofrecer VPSs a nuestro clientes. Cada cliente dispondrá de un servidor virtual (container) para construir su web. ¿Cómo podemos compartir el web hosting entre todos los VPSs?

De entre todas las opciones posibles vamos a hacerlo con Varnish configurado como Proxy

Segun Wikipedia:

Varnish Cache es un acelerador de aplicaciones web, también conocido como caché de proxy HTTP inversa. Se instala delante de cualquier servidor HTTP y se configura para almacenar en caché una copia del recurso solicitado. Ideado para aumentar el rendimiento de las aplicaciones web.

Lo primero que debemos hacer es instalar varnish

# apt-get install varnish

Debemos ajustar los parámetros para iniciar el demonio:

# vim /etc/default/varnish

Incluimos la configuración necesaria:

VARNISH_RUN_USER=varnish
VARNISH_RUN_GROUP=varnish

START=yes

# Maximum number of open files (for ulimit -n)
NFILES=131072
# Locked shared memory (for ulimit -l)
# Default log size is 82MB + header
MEMLOCK=82000
# Maximum number of threads (for ulimit -u)
NPROCS="unlimited"
# Maximum size of corefile (for ulimit -c). Default in Fedora is 0
# DAEMON_COREFILE_LIMIT="unlimited"

RELOAD_VCL=1

# # Should probably change this
VARNISH_VCL_CONF=/etc/varnish/default.vcl

# # Not setting VARNISH_LISTEN_ADDRESS makes Varnish listen on all IPs on this box
# # (Both IPv4 and IPv6 if available). Set manually to override this.
# VARNISH_LISTEN_ADDRESS=
VARNISH_LISTEN_PORT=80

# # Telnet admin interface listen address and port
VARNISH_ADMIN_LISTEN_ADDRESS=127.0.0.1
VARNISH_ADMIN_LISTEN_PORT=6082

# # Shared secret file for admin interface
VARNISH_SECRET_FILE=/etc/varnish/secret

# # The minimum number of worker threads to start
VARNISH_MIN_THREADS=50

# # The Maximum number of worker threads to start
VARNISH_MAX_THREADS=5000

# # Idle timeout for worker threads
VARNISH_THREAD_TIMEOUT=120

# Best option is malloc if you can. malloc will make use of swap space smartly if
# you have it and need it.
VARNISH_STORAGE_TYPE=malloc

# # Cache file size: in bytes, optionally using k / M / G / T suffix,
# # or in percentage of available disk space using the % suffix.
VARNISH_STORAGE_SIZE=2G

VARNISH_STORAGE="${VARNISH_STORAGE_TYPE},${VARNISH_STORAGE_SIZE}"

# # Default TTL used when the backend does not specify one
VARNISH_TTL=60

# # DAEMON_OPTS is used by the init script.  If you add or remove options, make
# # sure you update this section, too.
DAEMON_OPTS="-a ${VARNISH_LISTEN_ADDRESS}:${VARNISH_LISTEN_PORT} 
             -f ${VARNISH_VCL_CONF} 
             -T ${VARNISH_ADMIN_LISTEN_ADDRESS}:${VARNISH_ADMIN_LISTEN_PORT} 
             -t ${VARNISH_TTL} 
             -w ${VARNISH_MIN_THREADS},${VARNISH_MAX_THREADS},${VARNISH_THREAD_TIMEOUT} 
             -u ${VARNISH_RUN_USER} -g ${VARNISH_RUN_GROUP} 
             -S ${VARNISH_SECRET_FILE} 
             -s ${VARNISH_STORAGE}"

A continuación definimos la configuración del proxy:

backend default {
  .host = "127.0.0.1";
  .port = "80";
}

backend client1 {
        .host = "192.168.1.6";
        .port = "80";
}

backend client2 {
        .host = "192.168.1.7";
        .port = "80";
}

sub vcl_recv {
    set req.grace = 10s;
    
    if (req.http.host ~ "client1.example.com") {
      set req.backend = client1;
    } else if (req.http.host ~ "client2.example.com") {
      set req.backend = client2;
    } else {
      set req.backend = default;
    }
[...]

Comprobamos que la sintaxis está correcta:

# varnishd -C -f /etc/varnish/default.vcl

Si no devuelve ningún error, podemos reiniciar el demonio:

# service varnish restart

Directiva Satisfy

En el servidor Apache podemos restringir los accesos a nivel de hosts con las directivas allow y deny o a nivel de usuarios con Auth. El orden en el que se aplican estas directivas lo decidimos con la directiva Satisfy.

Así nos podemos encontrar en una situación en la que queramos restringir el acceso sólo desde nuestra red local y necesitemos que algunos usuarios conecten desde casa.

La directiva Satisfy, como comentamos, conmuta entre las restricción a nivel de host y la autenticación de usuarios.

El valor por defecto es All lo que significa que debe cumplir con todas las restricciones, para que no sea tan extricto cambiamos el valor a Any con el que permitimos que cumpla al menos una. De esta forma, en nuestro ejemplo, si estamos en la red local no preguntará contraseña porque cumplirá la restricción de hosts y si estamos en casa, deberemos responder al usuario y contraseña.

Nuestro fichero de configuración tendría una pinta parecida a esta:

AuthUserFile /etc/apache2/users
AuthName Sitio Local
AuthType Basic
Satisfy Any
<LIMIT GET POST>
order deny,allow
deny from all
allow from 192.168.
require user local
</LIMIT>

Fuente: Linux System Administration Recipes, Juliet Kemp, APress, 2009

Crear un Demonio en Linux

En esta entrada vamos a explicar cómo proceder para escribir un demonio bajo GNU/Linux. Lo primero es definir que se entiende por un demonio, y citamos a la wikipedia:

Un demonio, daemon o dæmon (de sus siglas en inglés Disk And Execution MONitor), es un tipo especial de proceso informático no interactivo, es decir, que se ejecuta en segundo plano en vez de ser controlado directamente por el usuario. Este tipo de programas se ejecutan de forma continua (infinita), vale decir, que aunque se intente cerrar o matar el proceso, este continuará en ejecución o se reiniciará automáticamente. Todo esto sin intervención de terceros y sin dependencia de consola alguna.

Un ejemplo de demonio es Apache, permanece en ejecución en segundo plano esperando peticiones de páginas web para ser servidas. O cron que es un demonio temporal, es decir, realiza acciones basadas en el tiempo.

El primer paso que debemos tener en cuenta es qué va a realizar el demonio, Linux basa su programación en que cada programa resuelve un único problema pero lo hace bien. Para ilustrar esta entrada construiremos un demonio que supervise si el demonio Apache está funcionando.

Requisitos

Necesitamos un compilador de C, normalmente GCC, y las cabezeras de C para linux, esto en Debian se consigue con:

# apt-get install build-essential

Necesitaremos también un editor para escribir el código.

Estructura básica de un demonio

Durante la ejecución del demonio debemos realizar algunas tareas básicas que se resumen en:

  • Hacer fork del proceso padre.
  • Cambiar la máscara de fichero.
  • Abrir los archivos de registros necesarios.
  • Crear un identificador de sesión único.
  • Cambiar el directorio de trabajo hacia un sitio más seguro.
  • Cerrar los descriptores de ficheros estandard.
  • Escribir el código del demonio propiamente.

El código

He comentado el código con lo más importante y está bastante autoexplicativo:


#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <syslog.h>
#include <string.h>

int main(void) {
  
  pid_t pid, sid;
  int apachePIDfile, apachePIDread, apachePID, daemonLogFileDesc;
  char buf[5], filename[255];        
          
  /* Forkeamos el proceso padre */
  pid = fork();
  if (pid < 0) {
    exit(EXIT_FAILURE);
  }
  /* Cuando tenemos un PID correcto podemos cerrar
   * el proceso padre.
   * Atención al control de errores, es una buena
   * técnica de programación comprobar todas las
   * situaciones donde se pueden dar errores. */
  if (pid > 0) {
    exit(EXIT_SUCCESS);
  }

  /* Cambiamos el modo de la mascara de ficheros */
  /* Hacemos esto para que los fichero generados por el
   * demonio sean accesibles por todo el mundo */
  umask(0);
          
  /* Abrimos los ficheros de logs del demonio */        
  /* Esto es opcional pero como vamos a cerrar los descriptores 
   * hacemos esto para que exista algo de comunicación con el demonio */
  daemonLogFileDesc = open ("log", O_WRONLY | O_CREAT, 0600);
  if (daemonLogFileDesc == -1) {
    perror ("Error en la apertura del log");
    exit (EXIT_FAILURE);
  }
  
  /* Creamos un nuevo SID */
  /* Esto se hace porque al haber matado al padre el hijo puede quedarse 
   * en el sistema como un proceso zombie, generando un nuevo SID hacemos
   * que el sistema se haga cargo del proceso huérfano otorgándole un nuevo SID */
  sid = setsid();
  if (sid < 0) {
    perror("new SID failed");
    exit(EXIT_FAILURE);
  }
    
  /* Por seguridad, cambiamos el directorio de trabajo */
  if ((chdir("/")) < 0) {
    perror("Change the current work directory failed");
    exit(EXIT_FAILURE);
  }
    
  /* Cerramos los descriptores standard */
  /* El demonio no puede usar la terminal, por lo que estos
   * descriptores son inútiles y un posible riesgo de seguridad.*/
  close(STDIN_FILENO);
  close(STDOUT_FILENO);
  close(STDERR_FILENO);
    
  /* El código del demonio */
  /* Obtenemos el PID con el que está corriendo el proceso apache */
  apachePIDfile = open("/var/run/apache2.pid", O_RDONLY, 0600);
  if (apachePIDfile == -1) {
    perror("Error en la apertura del fichero");
    exit(EXIT_FAILURE);
  }
  apachePIDread = read (apachePIDfile, buf, sizeof(buf));
    
  /* El gran bucle! */
  /* El demonio ejecutara este bucle toda su vida,
   * abrirá un archivo del pseudo sistema de ficheros /proc
   * y comprobará que existe, si existe Apache está corriendo y lo escribe
   * en el log, en caso contrario sale. */
  while (1) {
    apachePID = atoi (buf);
    snprintf(filename, sizeof(filename), "/proc/%d/cmdline", apachePID);
  
    if ((open (filename, O_RDONLY, 0600)) == -1) {
      perror ("No puedo abrir el fichero en proc");
      exit(EXIT_FAILURE);
    } else {
      write (daemonLogFileDesc, "Apache running\n", 15);
      sleep(30); /* espera 30 segundos */
    }
  }
  exit(EXIT_SUCCESS);
}

Compilamos

Para compilar hacemos lo siguiente:

# cc daemon1.c -o daemon1

A falta de un nombre más original, he llamado al demonio daemon1.

Ejecución

Para ejecutarlo hacemos:

# ./daemon1

Podemos ver que está corriendo tanto en el lista de procesos con ps como mirando el fichero log que hemos creado.

tailf log
...
Apache running
Apache running
Apache running
...

Referencias

Es una traducción/adaptación de la guía Linux Daemon Writing HOWTO, que es más teórica y aquí he querido dar una aplicación práctica, aunque es obvio que existe demonios de monitorización más avanzado quería marcar bien los conceptos de la construcción de un demonio aplicados a un caso particular.