Instalación y configuración de NextcloudPi con Docker o con la imagen completa de Nextcloud con Let's Encrypt y Traefik en Docker Compose en una Raspberry Pi

En esta entrada se instalará y se configurará Nextcloud para una Raspberry Pi. Se utilizará la versión dockerizada nextcloud, se instalará mediante docker-compose y el balanceo de carga se realizará con traefik. Tendremos como referencia el archivo docker-compose del repositorio heyValdemar/nextcloud-traefik-letsencrypt-docker-compose

¿Qué es Nextcloud?

Nextcloud es una serie de programas de código abierto, tanto el cliente como el servidor, con el objetivo de crear un servicio de alojamiento de archivos. Permite el alojamiento en un servidor propio, a modo de nube privada, para no tener la dependencia de nubes de terceros como Dropbox o Google Drive.

Requisitos

Instalar:

Redomendaciones

Actualizar el sistema

Lo primero siempre es actualizar el sistema con:

sudo apt update -y && sudp apt upgrade -y

Configurar cortafuegos ufw

Para instalar el cortafuegos ufw ejecutar:

sudo apt-get install ufw

Permitimos las conexiones por el puerto 80 (HTTP), 443(HTTPS), 2251(SSH configurado) y 4443(puerto de configuración de nextcloud).

sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 4443/tcp
sudo ufw allow 2251/tcp

Para activarlo:

sudo ufw enable

Para ver las reglas por defecto:

sudo ufw show raw

También se puede ver con un formato más legible:

sudo ufw status verbose

Apagar la Raspberry Pi de forma correcta

Para apagar la Raspberry Pi evitando que se corrompa la tarjeta SD se ejecuta el siguiente comando:

sudo shutdown -h now

Esperar a que la luz verde deje de parpadear y ya desenchufar el cable de alimentación.

Obtener dominio (Opcional)

Se requiere un dominio en caso de querer acceder desde el exterior de la red local, en otras palabras, desde Internet. Para ello existen varias páginas donde obtener dominios gratuitos o por menos de 1€ al año. Lo más importante es que el resgistrador de nombres de dominio tenga protección de Whois (WhoisGuard) para prevenir ataques de spam o de suplantación de indentidad a través de los datos personales que tienen que ser públicos al registrar un dominio en el ICANN.

En caso de usar un dominio de freenom, es necesario cambiar el DNS a un de Cloudflare siguiendo los pasos de este tutorial.

Desplegar NextcloudPi con Docker

Para levantar el contenedor docker de nextcloudpi, simplemente se necesitan los siguientes pasos:

  • Añadir la variable de entorno con el nombre del dominio:
export $DOMINIO=dominioejemplo.org
  • Levantar el contenedor desde el repositorio de Docker Hub:
docker run -d -p 4443:4443 -p 443:443 -p 80:80 -v ncdata:/data --restart unless-stopped --name nextcloudpi ownyourbits/nextcloudpi $DOMINIO

Activación de nextcloudpi

Escribir en el navegador la dirección http://<IP-Raspberry-Pi>. Aceptar el riesgo de seguridad. Aparecerá la ventana de activación de nextcloudpi. Es importante guardar las credenciales que se muestran, por ejemplo:

  • Nextcloudpi:
ncp
cVYu90pMiZ8GyglILiLVUlD67ID69AOCarv68l4l/so
  • Nextcloud user:
ncp
1CAhXuQgPEQ99/zfI5xeCNaE7cHeqZpjGhYnuBqVehI

Pulsar activar e introducir las credenciales de nextcloudpi. En la primera ejecución, si se quiere acceder al servidor desde la fuera de la red local, se pedirá que se realice el Port forwarding para los puerto 80 (HTTP) y 443(HTTPS) para la IP asignada a la raspberrypi. Se necesita acceder al router desde un navegador a la IP 192.168.1.1 mediante la contraseña del router, suele estar en una etiqueta debajo del router o por defecto es admin o 1234. Si no se permite desde la configuración del proveedor, será necesario realizarlo desde los ajustes avanzados del router.

Una vez realizado, o en caso de no que no quiera ser accesible desde fuera, se requiere ir al panel de configuración de nextcloudpi y en CONFIG->nc-trusted-domains. En caso de querer exponer la raspberry pi accesible desde internet, se recomienta seguir la entrada “Configuración de seguridad y privacidad un router” y continuar leyendo los siguientes apartados. De todas maneras, ya se puede acceder al nextcloud desplegado en la red LAN.

Aceso a nextcloud

Mediante el usuario ncp y la contraseña del Nextcloud user, ya se puede acceder al servidor de almacenamiento Nextcloud. Además tiene para calendario, notas, tareas y otras herramientas y aplicaciones que se pueden añadir como edición de textos de manera colaborativa, videollamadas, servidor de comunicación, mapas, reproductor de música y radio, gestor de contraseña, etc. Tiene una infinidad de opciones.

Desplegar la imagen completa de Nextcloud con Let’s Encrypt y Traefik en Docker Compose

Primero, creamos un archivo donde guardaremos las variables de entorno:

POSTGRES_HOST=postgres # Es el nombre del contenedor Docker de postgres
DB_PORT=5432 # Es el puerto para la base de datos postgres
POSTGRES_VERSION=13.3
POSTGRES_DB=nextcloud-db
POSTGRES_USER=postgres
POSTGRES_PASSWORD=
NEXTCLOUD_ADMIN_USER=nc_admin
NEXTCLOUD_ADMIN_PASSWORD=
DOMAIN=localhost # Si no teneos ningún dominio poner "localhost", sino "dominioejemplo.org"
NEXTCLOUD_TRUSTED_DOMAINS= "cloud.localhost localhost" # Añadir dominio y subdominio de la dirección de Nextcloud (si tenemos)
NEXTCLOUD_VERSION="24.0.1"

En segundo lugar, creamos un archivo docker-compose.yml con el siguiente contenido:

version: "3.3"

services:

  traefik:
    image: "traefik:v2.6"
    container_name: "traefik"
    command:
      - "--log.level=DEBUG"
      - "--api.insecure=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.websecure.address=:443"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.web.http.redirections.entryPoint.to=websecure"
      - "--entrypoints.web.http.redirections.entryPoint.scheme=https"
      - "--entrypoints.web.http.redirections.entrypoint.permanent=true"
      - "--certificatesresolvers.myresolver.acme.tlschallenge=true"
      - "--certificatesresolvers.myresolver.acme.caserver=https://acme-v02.api.letsencrypt.org/directory"
      - "--certificatesresolvers.myresolver.acme.email=hello@${DOMAIN}" # Email de ejemplo, cambiarlo para recibir correos de la renovación de los certificados
      - "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
    ports:
      - "443:443"
      - "8080:8080"
    volumes:
      - "./letsencrypt:/letsencrypt"
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
    networks:
      - nextcloud-network
    restart: unless-stopped

  postgres:
    # Image tag 
    image: postgres:${POSTGRES_VERSION}
    volumes:
      - ./nextcloud-postgres:/var/lib/postgresql/data
    env_file:
      - .env
    networks:
      - nextcloud-network

  nextcloud:
    # Image tag
    image: nextcloud:${NEXTCLOUD_VERSION}-apache
    volumes:
      - ./nextcloud-data:/var/www/html
    image: nextcloud
    networks:
      - nextcloud-network
    env_file:
      - .env

    labels:
      - "traefik.enable=true"
      # Nextcloud URL
      - "traefik.http.routers.nextcloud.rule=Host(`cloud.${DOMAIN}`)"
      - "traefik.http.routers.nextcloud.service=nextcloud"
      - "traefik.http.routers.nextcloud.entrypoints=websecure"
      - "traefik.http.services.nextcloud.loadbalancer.server.port=80"
      - "traefik.http.routers.nextcloud.tls=true"
      - "traefik.http.routers.nextcloud.tls.certresolver=myresolver"
      - "traefik.http.services.nextcloud.loadbalancer.passhostheader=true"
      - "traefik.http.routers.nextcloud.middlewares=compresstraefik"
      - "traefik.http.middlewares.compresstraefik.compress=true"
    restart: unless-stopped
    depends_on:
      - postgres
      - traefik

volumes:
  nextcloud-data:
  nextcloud-postgres:
  traefik-certificates:


networks:
  nextcloud-network:
    driver: bridge

Ya podemos buscar en el navegador nuestro dominio http://cloud.localhost (o si tenemos dominio se substituiría por http://cloud.dominioejemplo.org) o nuestra dirección http://<IP-Raspberry-Pi> y configurar nuestra servidra Nextcloud. En caso de querer tener configurada la raspberry de forma continuada en el tiempo y querer alojar más servicios en un futuro, recomendamos seguir la siguiente estructura de la configuración para facilitar la escalabilidad futura.

Configuración en producción de Nextcloud en una Raspberry Pi

Nota importante: no funciona sin un dominio.

Creamos una carpeta raiz que contendrá una carpeta con el nombre de cada autoalojemos, por ejemplo la llamaremos servicios-autoalojados:

mkdir servicios-autoalojados

Variables de entorno de Traefik

Una vez creado nos dirigidos al directorio y creamos otra carpeta llamada traefik. Además, añadimos un nuevo archivo .env en la carpeta traefik con el siguiente contenido. Es la variable de entorno necesaria para traefik. También se pueden incluir en cada carpeta raiz y tenerlas centralizadas en dicha carpeta, el inconveniene es que todos los contenedores tendrían acceso a todas las variables de entorno y eso podría dar lugar a problemas de seguridad. Por esa razón, recomendamos tener un archivo de variables de entorno separado por cada servicio que despleguemos.

DOMAIN=dominioejemplo.org # Cambiar por un dominio propio

Docker-compose de producción de Traefik

Después, creamos un archivo docker-compose.yml con la siguiente configuración:

version: "3.3"

services:

  traefik:
    image: "traefik:v2.6"
    container_name: "traefik"
    command:
      - "--log.level=DEBUG"
      # - "--api.insecure=false"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.websecure.address=:443"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.web.http.redirections.entryPoint.to=websecure"
      - "--entrypoints.web.http.redirections.entryPoint.scheme=https"
      - "--entrypoints.web.http.redirections.entrypoint.permanent=true"
      - "--certificatesresolvers.myresolver.acme.tlschallenge=true"
      - "--certificatesresolvers.myresolver.acme.caserver=https://acme-v02.api.letsencrypt.org/directory"
      # - "--certificatesresolvers.myresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory" # Staging URL of Let's Encrypt
      - "--certificatesresolvers.myresolver.acme.email=admin@${DOMAIN}" # Email de ejemplo, cambiarlo para recibir correos de la renovación de los certificados
      - "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
    ports:
      - "443:443"
      - "8080:8080"
    volumes:
      - "./letsencrypt:/letsencrypt"
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
    env_file:
      - .env
    networks:
      - traefik-external-network
      - nextcloud-network
    restart: unless-stopped 

volumes:
  letsencrypt:

networks:
  traefik-external-network:
    driver: bridge
    external: true
  nextcloud-network:
    driver: bridge

En el hemos creado una red de docker llamada traefik-external-network donde se expodrán los servicios al exterior y es obligatorio que sea de tipo external, mientras que nextcloud-network servirá para conectar la base de datos postgres con netxcloud.

Desplegamos el docker-compose con:

docker-compose up --build --force-recreate

Variables de entorno de Nextcloud, Postgres y Redis

A continuación, creamos una carpeta llamada nextcloud. Una vez creada nos dirigidos al directorio y añadimos un nuevo archivo .env en la carpeta nextcloud con el siguiente contenido. Son las variables de entorno necesarias para nextcloud y postgres. Es importante borrar los comentarios que se han añadido en el siguiente ejemplo para que funcione correctamente.

DOMAIN=localhost # Si no teneos ningún dominio poner "localhost"
SUBDOMAIN_NEXTCLOUD=cloud # Si no tenemos ningún dominio, nuestra dirección será cloud.localhost
POSTGRES_HOST=postgres # Es el nombre del contenedor Docker de postgres
DB_PORT=5432 # Es el puerto para la base de datos postgres
POSTGRES_VERSION=13.3
POSTGRES_DB=nextclouddb
POSTGRES_USER=postgres
POSTGRES_PASSWORD=
NEXTCLOUD_ADMIN_USER=nc_admin
NEXTCLOUD_ADMIN_PASSWORD=
NEXTCLOUD_TRUSTED_DOMAINS= "cloud.localhost localhost" # Añadimos nuestro dominio o si no teneos ningún dominio poner "localhost". La dircción por defecto del subdominio es cloud
NEXTCLOUD_VERSION="24.0.1"
REDIS_HOST=redis
TRUSTED_PROXIES=192.168.1.0/24
OVERWRITEPROTOCOL=https

Docker-compose de producción de Nextcloud y Postgres

Después, creamos un archivo docker-compose.yml con la siguiente configuración:

version: "3.3"

services:

  postgres:
    # Image tag 
    image: postgres:${POSTGRES_VERSION}
    container_name: postgres
    volumes:
      - ./nextcloud-postgres:/var/lib/postgresql/data
    env_file:
      - .env
    networks:
      - nextcloud-network
    restart: unless-stopped

  nextcloud:
    # Image tag
    image: nextcloud:${NEXTCLOUD_VERSION}
    container_name: nextcloud
    volumes:
      - ./nextcloud-data:/var/www/html
    image: nextcloud
    networks:
      - traefik-external-network
      - nextcloud-network
    env_file:
      - .env
    labels:
      - "traefik.enable=true"
      # Utilizar la red traefik-external-network que se define más abajo
      - "traefik.docker.network=traefik-external-network"  
      # Nextcloud URL
      - "traefik.http.routers.nextcloud.rule=Host(`${SUBDOMAIN_NEXTCLOUD}.${DOMAIN}`)"
      - "traefik.http.routers.nextcloud.service=nextcloud"
      - "traefik.http.routers.nextcloud.entrypoints=websecure"
      - "traefik.http.services.nextcloud.loadbalancer.server.port=80"
      - "traefik.http.routers.nextcloud.tls=true"
      - "traefik.http.routers.nextcloud.tls.certresolver=myresolver"
      - "traefik.http.services.nextcloud.loadbalancer.passhostheader=true"
      - "traefil.docker.network=traefik-external-network"
      ## Middlewares
      - "traefik.http.routers.nextcloud.middlewares=secHeaders,nextcloud_dav"

      # Security headers
      - "traefik.http.middlewares.secHeaders.headers.customFrameOptionsValue=SAMEORIGIN"
      - "traefik.http.middlewares.secHeaders.headers.framedeny=true"
      - "traefik.http.middlewares.secHeaders.headers.sslredirect=true"
      - "traefik.http.middlewares.secHeaders.headers.STSIncludeSubdomains=true"
      - "traefik.http.middlewares.secHeaders.headers.STSPreload=true"
      - "traefik.http.middlewares.secHeaders.headers.STSSeconds=315360000"
      - "traefik.http.middlewares.secHeaders.headers.forceSTSHeader=true"
      - "traefik.http.middlewares.secHeaders.headers.sslProxyHeaders.X-Forwarded-Proto=https"

      # WebDav
      - "traefik.http.middlewares.nextcloud_dav.redirectregex.permanent=true"
      - "traefik.http.middlewares.nextcloud_dav.redirectregex.regex=/.well-known/(card|cal)dav"
      - "traefik.http.middlewares.nextcloud_dav.redirectregex.replacement=/remote.php/dav/"
    restart: unless-stopped
    depends_on:
      - postgres
      - redis

  redis:
    image: redis:6.2-alpine
    container_name: redis
    hostname: redis
    restart: unless-stopped
    networks:
      - nextcloud-network
    volumes:
      - ./nextcloud-redis:/data

volumes:
  nextcloud-data:
  nextcloud-postgres:
  nextcloud-redis:

networks:
  traefik-external-network:
    driver: bridge
    external: true

  nextcloud-network:
    driver: bridge

Se añaden los siguientes middlewares para activar la sincronización utilizando el proxy inverso de Traefik. Las redirecciones para CalDAV o CardDAV no funcionan si Nextcloud se ejecuta detrás de un proxy inverso. La solución recomendada es que su proxy inverso haga las redirecciones. Para ello, hemos incluido las siguientes líneas de código en el docker-compose:

      ## Middlewares
      - "traefik.http.routers.nextcloud.middlewares=secHeaders,nextcloud_dav"

      # Security headers
      - "traefik.http.middlewares.secHeaders.headers.customFrameOptionsValue=SAMEORIGIN"
      - "traefik.http.middlewares.secHeaders.headers.framedeny=true"
      - "traefik.http.middlewares.secHeaders.headers.sslredirect=true"
      - "traefik.http.middlewares.secHeaders.headers.STSIncludeSubdomains=true"
      - "traefik.http.middlewares.secHeaders.headers.STSPreload=true"
      - "traefik.http.middlewares.secHeaders.headers.STSSeconds=315360000"
      - "traefik.http.middlewares.secHeaders.headers.forceSTSHeader=true"
      - "traefik.http.middlewares.secHeaders.headers.sslProxyHeaders.X-Forwarded-Proto=https"

      # WebDav
      - "traefik.http.middlewares.nextcloud_dav.redirectregex.permanent=true"
      - "traefik.http.middlewares.nextcloud_dav.redirectregex.regex=/.well-known/(card|cal)dav"
      - "traefik.http.middlewares.nextcloud_dav.redirectregex.replacement=/remote.php/dav/"

Desplegamos el docker-compose con:

docker-compose up --build --force-recreate

El certificado de Let’s Encrypt puede tardar unos minutos en obtenerse, por lo tanto, espere y si obtiene algún warning en los logs de Traefik, no es preocupante. Una forma para probar si se ha generado correctamente es leer el archivo acme.json de la carpeta letsencrypt creada en el directorio nextcloud. También se pueden recrear los servicios hasta que funcione todo correctamente.

Por último, ya podemos buscar en el navegador nuestro dominio https://cloud.dominiodeejemplo.org