Tu nube privada sobre Raspberry Pi con Owncloud, Nginx y MySQL

Es increíble todo lo que se puede hacer con un equipo tan pequeño y económico. Una de ellas es el despliegue de una Nube Privada con Owncloud sobre un Raspberry Pi® 2 Model B.

El primer paso, si no se hizo ya, necesariamente será la instalación del SO en el equipo, el (relativamente sencillo) proceso completo está descrito en este artículo previo.

La paquetería necesaria:

Lo primero por hacer es la instalación de los paquetes necesarios:

# dnf -y install nginx php-fpm mariadb-server wget php php-zip php-dom php-xml php-gd php-mysql php-mbstring tar bzip2

Si bien uno puede estar acostumbrado a que este proceso dure relativamente poco, en un Raspberry toma bastante más, así que no desesperarse y recordar que se gastó menos de 50usd en él y sin embargo está haciendo todo lo que hacen sus hermanos mayores ;-).

El servidor web:

Si bien el servidor que se pidió instalar fue nginx, al instalar el paquete «php» se instaló también httpd porque es dependencia de él. La única desventaja de que esté instalado y no se use para nada, es el espacio en disco que se usará, pero que no sobrepasa los 5MB, así que no es algo que me quite el sueño.

Lo primero es el php-fpm, que es el servicio que proporcionará la interpretación de los scripts PHP (owncloud está hecho en PHP). Por suerte este viene listo así que solo se arranca el servicio y verifica que esté corriendo revisando el archivo de sock que debe levantarse con él:

# systemctl enable php-fpm
# systemctl start php-fpm
# ls -l /run/php-fpm/www.sock

El primero activa el servicio para que arranque con el sistema si se apaga o reinicia, el segundo lo arranca en este momento y el tercero busca listar el archivo de socket de php-fpm para asegurar que fue creado y está listo para usarse, deberá salir algo como:

srw-rw----+ 1 root root 0 ago 16 14:56 /run/php-fpm/www.sock

A continuación editamos el archivo de configuración principal de nginx, para desactivar la configuración por defecto:

# vi /etc/nginx/nginx.conf

Buscamos la sección:

server {
    listen       80 default_server;
    listen       [::]:80 default_server;
    server_name  _;
    root         /usr/share/nginx/html;

    # Load configuration files for the default server block.
    include /etc/nginx/default.d/*.conf;

    location / {
    }

    error_page 404 /404.html;
        location = /40x.html {
    }

    error_page 500 502 503 504 /50x.html;
        location = /50x.html {
    }
}

Y la comentamos completa, añadiendo un # (hash tag) al inicio de cada línea

Ahora creamos el archivo de configuración para Owncloud:

# vi /etc/nginx/conf.d/owncloud.conf

Y anexamos el siguiente contenido, extraído del sitio de Owncloud y adaptado a la necesidad particular:

upstream php-handler {
    #server 127.0.0.1:9000;
    server unix:/run/php-fpm/www.sock;
}

server {
    listen 80;
    server_name owncloud.paulbernal.com;
    # enforce https
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl;
    server_name owncloud.paulbernal.com;

    ssl_certificate /etc/letsencrypt/live/paulbernal.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/paulbernal.com/privkey.pem;

    # Add headers to serve security related headers
    # Before enabling Strict-Transport-Security headers please read into this topic first.
    # add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;";
    add_header X-Content-Type-Options nosniff;
    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Robots-Tag none;
    add_header X-Download-Options noopen;
    add_header X-Permitted-Cross-Domain-Policies none;

    # Path to the root of your installation
    root /var/www/owncloud;

    location = /robots.txt {
        allow all;
        log_not_found off;
        access_log off;
    }

    # The following 2 rules are only needed for the user_webfinger app.
    # Uncomment it if you're planning to use this app.
    #rewrite ^/.well-known/host-meta /public.php?service=host-meta last;
    #rewrite ^/.well-known/host-meta.json /public.php?service=host-meta-json last;

    location = /.well-known/carddav {
        return 301 $scheme://$host/remote.php/dav;
    }
    location = /.well-known/caldav {
        return 301 $scheme://$host/remote.php/dav;
    }

    location /.well-known/acme-challenge { }

    # set max upload size
    client_max_body_size 512M;
    fastcgi_buffers 64 4K;

    # Disable gzip to avoid the removal of the ETag header
    gzip off;

    # Uncomment if your server is build with the ngx_pagespeed module
    # This module is currently not supported.
    # pagespeed off;

    error_page 403 /core/templates/403.php;
    error_page 404 /core/templates/404.php;

     location / {
        rewrite ^ /index.php$uri;
    }

    location ~ ^/(?:build|tests|config|lib|3rdparty|templates|data)/ {
        return 404;
    }
    location ~ ^/(?:\.|autotest|occ|issue|indie|db_|console) {
        return 404;
    }

    location ~ ^/(?:index|remote|public|cron|core/ajax/update|status|ocs/v[12]|updater/.+|ocs-provider/.+|core/templates/40[34])\.php(?:$|/) {
        fastcgi_split_path_info ^(.+\.php)(/.*)$;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
        fastcgi_param HTTPS on;
        fastcgi_param modHeadersAvailable true; #Avoid sending the security headers twice
        fastcgi_param front_controller_active true;
        fastcgi_pass php-handler;
        fastcgi_intercept_errors on;
        fastcgi_request_buffering off;
    }

    location ~ ^/(?:updater|ocs-provider)(?:$|/) {
        try_files $uri/ =404;
        index index.php;
    }

    # Adding the cache control header for js and css files
    # Make sure it is BELOW the PHP block
    location ~* \.(?:css|js)$ {
        try_files $uri /index.php$uri$is_args$args;
        add_header Cache-Control "public, max-age=7200";
        # Add headers to serve security related headers (It is intended to have those duplicated to the ones above)
        # Before enabling Strict-Transport-Security headers please read into this topic first.
        # add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;";
        add_header X-Content-Type-Options nosniff;
        add_header X-Frame-Options "SAMEORIGIN";
        add_header X-XSS-Protection "1; mode=block";
        add_header X-Robots-Tag none;
        add_header X-Download-Options noopen;
        add_header X-Permitted-Cross-Domain-Policies none;
        # Optional: Don't log access to assets
        access_log off;
    }

    location ~* \.(?:svg|gif|png|html|ttf|woff|ico|jpg|jpeg)$ {
        try_files $uri /index.php$uri$is_args$args;
        # Optional: Don't log access to other assets
        access_log off;
    }
}

Como se podrá deducir, la configuración está hecha para trabajar por HTTPS, por lo cual se requiere disponer de un certificado de seguridad digital. En mi caso, el certificado es 100% real, obtenido de forma bastante sencilla y además gratis a través del proyecto Let’s Encrypt. En este artículo anterior se explica en detalle todo el proceso.

También se puede usar un certificado auto-generado, para ello se puede seguir la sección Step One — Create the SSL Certificate de este artículo de DigitalOcean. El único inconveniente en este caso será el de pasar a través de las advertencias iniciales que hacen los navegadores al entrar al sitio, anunciando de que no se está usando un certificado de confianza.

Sólo queda arrancar el servidor web con:

# systemctl enable nginx
# systemctl start nginx

El Firewall

Si ya disponemos de un servidor web activo y totalmente funcional, el firewall por defecto mantiene cerrados todos los puertos (excepto unos pocos) así que hay que abrirle los necesarios para que el servidor web tenga comunicación con el mundo exterior:

# firewall-cmd --add-service=http
# firewall-cmd --add-service=https
# firewall-cmd --add-service=http --permanent
# firewall-cmd --add-service=https --permanent

Las primeras dos líneas abren los puertos en el firewall y las segundas dos le indican que esta configuración será permanente, es decir persistirá luego de un reinicio/apagado del sistema.

Para probar basta con abrir el sitio en un navegador web, en mi caso la dirección era https://owncloud.paulbernal.com y deberá simplemente producir una página genérica que dice 404 Not Found, pero que más abajo indica nginx/1.8.1 con lo que se sabe que el nginx está trabajando. El error se da porque aún no existe el directorio /var/www/owncloud referido en la configuración, esto no es un problema por ahora y se resolverá más adelante.

La base de datos:

Si bien Owncloud puede trabajar con SQLite, ellos mismo desaniman su uso porque lo consideran poco performante y en su lugar promueven el uso de MySQL. Dado que en el primer paso ya se instaló todo lo necesario, sólo hay que empezar arrancando el servidor de base de datos:

# systemctl enable mariadb
# systemctl start mariadb

MariaDB es el nombre de la versión (fork) libre de MySQL que viene en las distros actuales y que se dió a raíz de la compra de MySQL por parte de Oracle. Por razones de comodidad, en lo futuro usaré MySQL aunque me esté refiriendo al MariaDB (después de todo, en el fondo y para fines prácticos, es lo mismo).

Si bien no es obligatorio, es muy recomendable correr el siguiente comando para realizar el aseguramiento básico del servidor de base de datos:

# mysql_secure_installation

La primera pregunta solicita la clave de root, se deja en vacío, la siguiente pregunta si se desea cambiar la clave de root, respondemos que sí «Y» y tipeamos dos veces la nueva clave de root. El resto de preguntas basta con presionar Enter (Intro) para aceptar las respuestas por defecto y listo! ya tenemos asegurado básicamente el servidor de BD.

Ahora hay que crear el usuario y la BD que se usará por parte del Owncloud, para ello entramos al MySQL:

# mysql -u root -p

Nos solicita la clave y le ponemos la misma que habíamos especificado en el paso anterior, con lo cual ingresamos a la consola del MySQL que se ve similar a esto:

...
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]>

En esta consola ejecutamos las siguientes sentencias SQL (una a la vez):

MariaDB [(none)]> CREATE USER 'owncloud'@'localhost' IDENTIFIED BY 'ponerLaClave'
MariaDB [(none)]> GRANT USAGE ON *.* TO 'owncloud'@'localhost' IDENTIFIED BY 'ponerLaClave';
MariaDB [(none)]> CREATE DATABASE IF NOT EXISTS `owncloud` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
MariaDB [(none)]> GRANT ALL PRIVILEGES ON `owncloud`.* TO 'owncloud'@'localhost';

Con lo cual: Se crea el usuario y se le asigna una clave, se crea la BD y se le otorgan los privilegios necesarios. No olvidar reemplazar ponerLaClave por la clave que se desee asignarle al usuario owncloud. Para salir de la consola de MySQL basta con ejecutar el comando quit.

El Owncloud:

El último paso es poner y configurar el Owncloud propiamente, lo primero es bajar el paquete desde el sitio oficial, al momento la versión más moderna es la 9.1.0:

# cd /var/www
# wget https://download.owncloud.org/community/owncloud-9.1.0.tar.bz2

Y descomprimir el paquete con:

# tar jxf owncloud-9.1.0.tar.bz2
# chown apache:apache ./owncloud -R

Lo cual creará una carpeta llamada owncloud, y ya dispondríamos del directorio web /var/www/owncloud referenciado en la configuración de nginx. El segundo comando establece como propietario del directorio al usuario apache para que la aplicación sea capaz de trabajar normalmente. En este punto se podría eliminar el archivo .tar.bz2 pues ya no se requeriría.

Listo! solo queda abrir el navegador con la misma dirección que habíamos probado el nginx antes y veremos la página inicial de configuración del Owncloud.

Es bastante simple, en la primera sección se solicita el usuario y la clave de administración para el sitio, poner aquí lo que se considere más apropiado.

Luego se pide la ruta del directorio de datos del owncloud, que es donde físicamente se almacenarán los archivos, no hace falta modificarlo y dejamos el dato tal cual ya se sugiere ahí mismo.

Más abajo, al seleccionar MySQL como motor de BD, se solicita el nombre de usuario, la contraseña, el nombre de la BD y el nombre del servidor que deberá usar el Owncloud. Aquí ingresamos los datos proporcionados al momento de crear la BD en el MySQL, del ejemplo los datos serían:

  • owncloud
  • ponerLaClave
  • owncloud
  • localhost

Al dar clic en el botón de Finish Setup, se realizan los pasos correspondientes y finales (esto toma unos momentos), luego se nos redirige a la página inicial del Owncloud … a celebrar, hemos terminado!

P.D. Si bien ya disponemos de un sistema totalmente funcional, el espacio de almacenamiento es bastante limitado pues se está trabajando sobre la SD del Raspbberry que más pronto que tarde podría resultar insuficiente. Para ello se puede implementar un sistema de almacenamiento adicional como en el artículo sobre la implementación de un NAS en Raspberry.