miércoles, 13 de julio de 2016

Como crear un servidor web en la red local accesible desde Internet

Hace demasiado tiempo que no escribo nada. Tengo miles de excusas (todas ciertas), pero el caso es que todas ellas se resumen en una: demasiado trabajo. Hoy voy a intentar escribir un artículo sobre como crear un servidor web en la red local accesible desde Internet con Apache2.

Con la mejora de las conexiones que están ofreciendo los operadores de telefonía en los últimos tiempos (banda ancha, fibra óptica, 100/10 MB, 300/30 MB, 300 MB simétricos, ...), cualquiera puede plantearse crear sus propios servicios web sin contratar ningún tipo de hosting. Es decir a coste cero. Bueno cero no porque siempre tendrás que pagar la conexión, pero eso lo ibas a pagar igual, ¿no?

Prerequisitos

Lo primero y más importante es disponer de conexión a Internet de banda ancha en casa. Idealmente fibra óptica simétrica. También es indispensable tener un PC que pueda estar conectado a la red todo el día. El tipo de PC es irrelevante, pero lo idóneo sería que tuviera un sistema operativo de tipo GNU/Linux. Por supuesto todo lo que voy a explicar se puede hacer con Windows, pero a partir de aquí usaré ejemplos para Ubuntu 14.04 que es una distribución de Linux muy popular. También sería muy interesante disponer de una IP estática, aunque no es obligatorio.

Instalar el software requerido

  • Apache2: Es el servidor HTTP más usado en el mundo. Es un programa de código abierto, multiplatafortma (Linux, Macintosh, Windows, ...), que implementa el protocolo HTTP/1.12. Es altamente configurable y permite utilizar multitud de módulos que añaden capacidades al servidor: SSL, Rewrite, PHP, Python, Perl, ...
    $ sudo apt-get install apache2
    $ apache2 -v
    
  • PHP, Python, Perl u otro lenguaje de programación similar. Yo opto por PHP que es muy fácil de usar y muy rápido de aprender, pero cualquier otro es válido. Para usar otros lenguajes como Java se necesitan herramientas diferentes.
    $ sudo apt-get install php5 libapache2-mod-php5
    $ php5 -v
    
  • Una base de datos como MySQL siempre es recomendable, aunque no obligatoria. Depende de los servicios que quieras ofrecer será imprescindible.
    $ sudo apt-get install mysql-server mysql-client php5-mysql
    $ mysql -V
    

Programar el sitio web

Para este paso se necesitan conocimientos de programación. En cualquier caso vamos a crear un sencillo "Hola Mundo" en PHP. Lo primero es editar un fichero de texto en la siguiente ruta:

$ cd /var/www/html/
$ mkdir hello-world
$ cd hello-world
$ gedit index.php

...y escribir el siguiente contenido:

<?php
echo "Hello World!";

Para ver que todo funciona correctamente hay que abrir un navegador e introducir la ruta: http://localhost/hello-world/index.php

Si todo ha ido bien veremos nuestro Hola Mundo en la pantalla del navegador. Si no funciona probablemente haya algún problema con Apache2. Por ejemplo que no se haya iniciado correctamente el servicio:

$ sudo service apache2 start

Acceso desde la red local

En este momento, si tienes otro dispositivo conectado a la misma red local (da igual si es un ordenador, un móvil o un tablet, si es por cable o por wifi), podrás acceder al servidor web del primero usando tu navegador favorito, poniendo la dirección IP del ordenador al que hemos instalado Apache2. Para ello lo primero es averiguar la dirección IP privada del ordenador con el servidor web. Podemos escribir ifconfig en el terminal, pero como sale mucha más información, también podemos usar comandos más complejos, pero que obtienen exactamente la información que necesitamos: La dirección IP:

$ ip route get 8.8.8.8 | head -1 | cut -d' ' -f8
192.168.1.38

En este caso la dirección IP de nuestro servidor es 192.168.1.38. Ojo que esa dirección puede cambiar cada ver que se reinicia el router. Entonces escribimos la siguiente ruta en otro dispositivo (PC, tablet o móvil): http://192.168.1.38/hello-world/index.php

Podría darse el caso de que Apache2 estuviera configurado para rechazar las conexiones desde fuera del localhost. En ese caso obtendríamos un mensaje como el siguiente:

Las causas de ese problema pueden ser muy diversas dependiendo de la versión de Apache2 y proponer soluciones a este problema se escapa del propósito de este artículo. En cualquier caso siempre se le puede preguntar a Google. La solución probablemente pase por editar el archivo /etc/apache2/apache2.conf y eliminar las restricciones de acceso, dejando algo similar a lo siguiente:

<Directory /var/www/>
    AllowOverride None
    Require all granted
</Directory>

Acceso desde Internet

Para acceder a Internet el router usa una IP pública, la misma para toda la red local. Eso quiere decir que todos los dispositivos conectados a la red local usan la misma dirección IP para conectarse a Internet, ya que todos tienen la misma dirección IP pública. Entonces, ¿Como podemos acceder desde el exterior a nuestro servidor web si varios dispositivos comparten la misma IP? La respuesta es que hay que configurar los puertos de red en el router. Es lo que se conoce como abrir puertos.

Apache2 usa por defecto el puerto 80 para el protocolo HTTP, entonces lo que habría que hacer es decirle al router que todas las conexiones que lleguen por el puerto 80 nos las redireccione a la máquina donde tenemos el servidor web que, recordemos, es la 192.168.1.38.

Para poder acceder desde Internet a nuestro servidor web es necesario conocer nuestra IP pública. No la privada que usa nuestro equipo dentro de la red local, sino la pública que es la que usa nuestro router para acceder a Internet y que puede ser fija (siempre la misma) o variable (distinta cada vez que se reinicia el router). Para averiguar nuestra IP pública podemos ir a cualesmiip.com y consultarlo o bien podemos escribir la siguiente instrucción en el terminal:

$ curl ifconfig.me
88.20.46.249

Nuestro servidor web ya es accesible desde Internet pero aun hay varios temas que solucionar:

  1. Cada vez que se reinicie el router la IP privada asignada a nuestro servidor puede cambiar, con lo que éste dejará de ser accesible desde Internet ya que nuestro router seguirá redireccionando las peticiones que lleguen por el puerto 80 a la IP 192.168.1.38 pero a lo mejor ahora nuestro servidor tiene otro. Lo que vamos a hacer es poner una IP privada fija a nuestro servidor.
  2. En segundo lugar abrir el puerto 80 no es lo más inteligente a no ser que queramos hacer una web abierta a todo el mundo. Lo mejor es usar un puerto diferente, dejando el puerto 80 cerrado.
  3. Por último la IP pública de nuestro router también podría cambiar cada vez que se reinicie por lo que no sabríamos que IP poner en el navegador. La única solución a esto es contratar una IP pública estática a nuestro proveedor de Internet.

Asignar una IP privada fija a nuestro servidor web

El router utiliza el protocolo DHCP para asignar las direcciones IP privadas a los distintos dispositivos conectados a la red local. De esta forma cada dispositivo tendrá un IP diferente cada vez que se reinicie el router. Sin embargo es posible asignar de forma sencilla una dirección IP fija (siempre la misma) a un dispositivo determinado. Por desgracia cada dispositivo y cada router tiene su forma particular de hacerlo. Mirando en la configuración de mi router he visto que las direcciones que asigna DHCP para la mi red local son de tipo 192.168.1.x, donde x > 32. Si x=1 es la dirección del router (default gateway), eso quiere decir que tengo libres las direcciones de la 2 <= x <= 32. Yo he elegido la IP 192.168.1.16 sin un motivo concreto.

Para asignar una IP fija en Ubuntu hay que ir a la Configuración del Sistema >>> Red >>> Cableada o Inalámbrica (depende) >>> Opciones... >>> Ajustes IPv4, seleccionar Método manual y rellenar los campos como en la imagen.

Cambiar el puerto de nuestro servicio web

Como hemos dicho el protocolo HTTP suele usar el puerto 80, pero si sólo queremos acceder nosotros a nuestro servicio web podemos configurar Apache2 para que use cualquier puerto en el rango 1-65535. Algunos puertos son usados por otros servicios, como por ejemplo:

  • 20=FTP-data
  • 21=FTP-control
  • 22=SSH
  • 23=Telnet
  • 25=SMTP
  • 80=HTTP
  • 110=POP3
  • 143=IMAP
  • 443=HTTPS-SSL
  • 465=SMTP-SSL
  • 993=IMAP-SSL
  • 995=POP3-SSL
  • 3306=MySQL
  • 8080=Alt-HTTP
  • Ver lista completa

En principio podemos usar cualquier puerto libre. Por ejemplo podemos usar el puerto 8010. Para ello hay que seguir varios pasos:

  1. Decir a Apache2 que tiene que escuchar el puerto 8010. Para ello, editar el archivo /etc/apache2/ports.conf y añadir la línea Listen 8010:
    $ sudo gedit /etc/apache2/ports.conf
    
    Listen 80
    Listen 8010
    <IfModule ssl_module>
        Listen 443
    </IfModule>
    <IfModule mod_gnutls.c>
        Listen 443
    </IfModule>
    
  2. Añadir un virtual host que escuche el puerto 8010. Para ello crear el archivo /etc/apache2/sites-available/001-my-web-server.conf:
    $ sudo gedit /etc/apache2/sites-available/001-my-web-server.conf
    
    <VirtualHost *:8010>
        ServerAdmin webmaster@localhost
        DocumentRoot /var/www/html/hello-world
        ErrorLog ${APACHE_LOG_DIR}/error-simple-proxy.log
        CustomLog ${APACHE_LOG_DIR}/access-simple-proxy.log combined
        DirectoryIndex index.php index.html index.htm index.cgi
    </VirtualHost>
    
  3. Activar el nuevo virtual host:
    $ cd /etc/apache2/sites-enabled/
    $ sudo ln -s ../sites-available/001-my-web-server.conf
    
  4. Reiniciar el servicio de Apache2:
    $ sudo service apache2 restart
    

miércoles, 24 de febrero de 2016

Introducción a Git

Pongo a continuación una presentación sobre Git, el sistema de control de versiones, y las diferencias con Subversion. Hice esta presentación como soporte a un workshop sobre Git en mi trabajo actual.

jueves, 21 de enero de 2016

Clasificación de lenguajes de programación en 2015

Infographic in DZone.com

Según Dzone.com:

  1. Javascript
  2. Java
  3. Python
  4. PHP
  5. C
  6. C++
  7. Ruby
  8. C#
  9. Objective-C
  10. Perl
  11. Css 3
  12. SQL
  13. Bash
  14. R
  15. Scala
  16. Matlab
  17. Visual Basic
  18. Haskell
  19. Go
  20. .Net
  21. Swift
  22. Clojure
  23. Groovy

domingo, 3 de enero de 2016

Menú de inicio desaparecido en Windows 10

Vamos con la primera entrada del año.

No acostumbro a usar sistemas Windows pero tengo un PC con Windows 10 para juegos1, para hacer pruebas y porque siempre conviene saber como va todo. Como decía, suelo usar ese PC para jugar por lo que es una máquina potente (Core i7, 16 GB de RAM, GTX 750 Ti, etc.), lo convierte en ideal para probar diferentes distribuciones de Linux en máquina virtual. Así que estaba yo el otro día tratando de instalar Xubuntu 15.10 en VirtualBox cuando sin saber bien como me cargué el menú de inicio, la barra de tareas y buena parte de la aplicaciones de Windows 10.

El escritorio funcionaba perfectamente, pero no podía acceder al menú de inicio, no aparecían los programas abiertos en la barra de tareas, el reloj y los iconos de la barra de estado habían desaparecido y los programas nuevos tampoco funcionaban (configuración del sistema, visor de fotos, etc.). En cambio podía ejecutar sin problema Firefox o Chrome e incluso el propio Virtual Box. También funcionaban los juegos (por ejemplo World of Warcraft). Reiniciar el sistema no sirvió para nada así que me puse a investigar.

Buscando buscando he encontrado que es un problema bastante común que lleva ocurriendo desde las primeras versiones preview de Windows 10. Hay varios artículos que tratan del tema como este, este, este, este o este, pero ninguna de las soluciones propuestas parecían funcionar.

Al final encontré el artículo Has your Windows 10 Start menu stopped working? Here are four ways to fix it que es una recopilación de todas las técnicas que puedes aplicar para solucionar el problema de menos agresiva a más agresiva. Al final lo que me funcionó a mi fue crear un nuevo usuario desde el Poweshell, reiniciar el PC y entrar con el nuevo usuario, pero ya llevaba probadas unas cuantas cosas por lo que bien pudo ser una combinación de factores.

Notas

[1] La principal función de ese PC es jugar y tiene Windows porque, reconozcámoslo, en Linux jugar es difícil. Steam es un gran avance pero configurar correctamente los drivers de la tarjeta gráfica es misión imposible. Ni los controladores libres ni los privativos funcionan como lo hacen en Windows. Y eso suponiendo que existan controladores para tu tarjeta gráfica.

viernes, 11 de diciembre de 2015

PHP 7.0.0 liberado

Acaba de hacerse oficial el lanzamiento de PHP 7.0.0. La nueva versión del lenguaje incluye numerosas mejoras y sobre todo un aumento de rendimiento considerable. Algunas de las nuevas características de PHP 7 son:

  • Tipo de retorno en las funciones: Por fin se podrá declarar que devuelve una función o método. Esta es una característica que siempre he echado en falta en PHP. Sobre todo cuando se diseñan interfaces o clases abstractas, poder indicar que debe devolver un método es algo muy útil. Ejemplos.
  • Declaración de tipos escalares: Se trata de poder definit el tipo de una variable para int, float, string y bool. Esto es útil por ejemplo para definir los parámetros de una función o método. También se definen dos niveles de exigencia: normal y estricto. El segundo se controla mediante la directiva declare(strict_types=1); (en la primera línea del script) e implica un control estricto de los tipos escalares provocando errores de tipo E_RECOVERABLE_ERROR en caso de incumplimiento. Ejemplos.
  • Nuevo operador ?? (coalesce): El operador de doble interrogación ?? permite devolver un valor específico si una variable no está definida. Por ejemplo $username = $_GET('username') ?? null; sería equivalente a $username = isset($_GET('username')) ? $_GET('username') : null;. Ejemplos.
  • Nuevo operador de comparación <=> (spaceship): La operación $a <=> $b devuelve 0 si $a y $b son iguales; devuelve -1 si $a es menor que $b; y devuelve 1 si $a es mayor que $b. Es decir lo mismo que strcmp() pero no limitado a strings. Funciona con variables de tipo int, float, string, array e incluso object. Ejemplos.
  • Sintaxis uniforme de variables: Se ha añadido soporte completo a construcciones de variables complejas, pero con consistencia. Por ejemplo [$obj1, $obj2][0]->prop, $foo->bar()(), (function() { ... })(), "string"->toLower(), etc. Ejemplos.
  • Solución de errores históricos.

Enlaces

miércoles, 2 de diciembre de 2015

Ya está aquí Symfony 3

SensioLabs ha anunciado esta semana la liberación de dos nuevas versiones del popular framework Symfony: La 2.8, que es la última versión de Symfony 2 y cuenta con soporte extendido; y la esperada versión 3.0 que inaugura la tercera iteración del framework PHP.

La versión 2.8 cuenta con retrocompatibilidad con las anteriores versiones de Symfon 2, mientras que la versión 3.0 elimina todas las funciones obsoletas (deprecated) que se han ido sustituyendo en las sucesivas versiones de Symfony 2. Es decir en esencia las versiones 2.8 y 3.0 de Symfony son idénticas excepto que la versión 2.8 mantiene la retrocompatibilidad con las versiones anteriores mientras que la 3.0 no lo hace.

La liberación de las nuevas versión hace que actualmente haya cinco versiones de Symfony con soporte:

Versión LTS Liberación Soporte bugs Soporte seguridad
2.3 x MAY-2013 MAY-2016 MAY-2017
2.6 NOV-2014 JUL-2015 ENE-2016
2.7 x MAY-2015 MAY-2018 MAY-2019
2.8 x NOV-2015 NOV-2018 NOV-2019
3.0 NOV-2015 JUL-2016 ENE-2017

La siguiente versión LTS (Long Time Suport) de Symfony está previsto que sea la 3.3 y la fecha prevista para su liberación es Mayo de 2017. Por ello si hay que empezar un proyecto nuevo con Symfony lo recomendable por ahora es usar la versión 2.8 que tiene 5 años de soporte, teniendo cuidado de no usar funciones obsoletas de Symfony 2 de forma que en el futuro sea factible realizar la migración a Symfony 3.

Enlaces

jueves, 19 de noviembre de 2015

¿Como evitar bloqueos de sesión en PHP?

PHP

Por defecto PHP escribe los datos de la sesión en un fichero en disco. La función session_start() bloquea el fichero de la sesión de forma que nadie más puede leerlo. Ese bloqueo durará hasta que se acaba la ejecución del script o hasta que se libere manualmente usando alguna de las siguientes funciones: session_abort(), session_commit(), session_destroy() o session_write_close().

El problema

La mayoría de los programadores inician la sesión al principio del script, pero la dejan abierta hasta el final. Eso en entornos con múltiples peticiones sobre la misma sesión (por ejemplo con mucho AJAX) es posible que acabe causando un cuello de botella en momentos putuales. En el momento que una petición tarde demasiado en responder (debido por ejemplo a una query complicada) las demás peticiones no podrán acceder al fichero de sesión que estará bloqueado con el resultado de un tiempo de respuesta significativamente elevado. Pero sólo en momentos puntuales.

La solución

Lo más fácil es liberar el fichero de sesión justo después del session_start(). Los datos de sesión seguirán disponibles para lectura a través de la variable superglobal $_SESSION, pero no se podrá encribir nada en la sesión.

<?php
// Start the session
session_start();

// Session is open to read/write ops
$_SESSION['latestRequestTime'] = time();

// Close the session
session_write_close();

// Now session is read-only
$user = $_SESSION['userNAme'];

Alternativas

Si no es posible cerrar la sesión justo después de abrirla (por ejemplo porque haya que escribir datos después de obtenerlos y/o procesarlos) hay algunas alternativas que pueden utilizarse: Guardar la sesión en una base de datos MySQL, ya que el motor de base de datos no bloquea la sesión, o bien guardar la sesión en algún sistema de caché como Redis, Memcache, etc.

Enlaces