Nextcloud con AppJail

Intro a Appjail

Slogan

Es una tool desarrollada por @DtxdF directamente de la mismísima patria 🇻🇪, dicha tool nos permite administrar nuestros jail en FreeBSD, con una cantidad de settings bastante amplia, e incluso fue inspirado en docker.

Esta es una lista donde se comparan las otras tools para administrar jails donde vemos que Appjail ofrece sus ventajas sin quedarse atrás.

Instalación

Con pkg install -y appjail la instalamos, pero si se quiere tener la versón mas actual debemos compilar desde el repo de github como lo indica la documentación.


Todas las imagenes actuales de AppJail

Respositorio publico de MakeJails

El repositorio centralizado


Habilitando column y printenv

En XigmaNAS, necesitamos printenv pero por alguna razón no esta, ya es cosa de los devs por diseño.

which printenv
servernas: appjail# which printenv
printenv: shell built-in command.

Listando nuestros jails

servernas: ~# appjail jail list (1)
/usr/local/bin/appjail: column: not found
1 Al intentar listar nuestros jails tenemos este error

Lo resolvemos con esto:

cd /tmp
mkdir -p repair
cd ./repair/
fetch https://download.freebsd.org/releases/amd64/13.2-RELEASE/base.txz
tar -xvf base.txz ./usr/bin/column ./usr/bin/mkfifo ./usr/bin/printenv
cp -i usr/bin/column /usr/bin/column     (1)
cp -i usr/bin/mkfifo /usr/bin/mkfifo     (2)
cp -i usr/bin/printenv /usr/bin/printenv (3)
1 Habilitando column
2 Habilitando mkfifo
3 Habilitando printenv

Configurando el Packet Filter

Incialmente con xigmanas tenemos este error

servernas: ~# service pf restart
Disabling pf.
Enabling pfpfctl: : No such file or directory
pfctl: cannot open the main config file!: No such file or directory
pfctl: Syntax error in config file: pf rules not loaded
/etc/rc.d/pf: WARNING: Unable to load .
sysrc pf_enable="YES"
sysrc pflog_enable="YES"
cat << "EOF" >> /etc/pf.conf
nat-anchor 'appjail-nat/jail/*'
nat-anchor "appjail-nat/network/*"
rdr-anchor "appjail-rdr/*"
EOF
service pf restart
service pflog restart

Y lo resolvemos con:

sysrc pf_rules=/etc/pf.conf
service pf restart
Disabling pf.
Enabling pf. (1)
1 Esta ok.

Esto también es importante

sysrc gateway_enable="YES"
sysctl net.inet.ip.forwarding=1

Ajustando el resolv.conf

Tenemos en el jail creado por appjail un enlace simbólico al /etc/resolv.conf pero incorrecto.

Entonces debemos borrarle y creamos de nuevo con los dns correctos, para lograr tener internet en el.

La mejor manera es editar directamente el fichero appjail.conf antes de crear el jail dicho fichero esta en nuestro host y de aquí se copiará al jail al crearse.
DEFAULT_RESOLV_CONF=/usr/local/etc/appjail/resolv.conf (1)
1 Aquí en esta ruta creamos el fichero resolv.conf con los dns necesarios

Mi fichero actual esta así luego de configurarlo correctamente:

servernas: ~# appjail cmd jexec nextcloud cat /etc/resolv.conf
domain local
nameserver 1.1.1.1
nameserver 1.0.0.1
existe una manera más eficiente de crearlo con el MakeJail muy parecido al dockerfile

Configurando el appjail.conf

/usr/local/etc/appjail/appjail.conf
EXT_IF=lagg0 (1)
ON_IF=$EXT_IF
FREEBSD_VERSION=13.2-RELEASE (2)
FREEBSD_ARCH=amd64
IMAGE_ARCH=amd64
SHORTEN_DOMAIN_NAMES=1
# Descomentalo si usas ZFS y deseas que AppJail tome ventaja de él:
ENABLE_ZFS=1
DEFAULT_RESOLV_CONF=/usr/local/etc/appjail/resolv.conf
1 Importante establecer nuestra interface de red aquí con ifconfig podemos verla
2 También cambiar la RELEASE que se ajuste en este momento.
(1)
lagg0: flags=8943<UP,BROADCAST,RUNNING,PROMISC,SIMPLEX,MULTICAST> metric 0 mtu 1500
	options=4a520b9<RXCSUM,VLAN_MTU,VLAN_HWTAGGING,JUMBO_MTU,VLAN_HWCSUM,WOL_MAGIC,VLAN_HWFILTER,VLAN_HWTSO,RXCSUM_IPV6,NOMAP>
	ether 00:25:90:2a:fb:52
  inet 192.168.1.250 netmask 0xffffff00 broadcast 192.168.1.255
	laggproto lacp lagghash l2,l3,l4
	laggport: igb0 flags=1c<ACTIVE,COLLECTING,DISTRIBUTING>
	laggport: igb1 flags=1c<ACTIVE,COLLECTING,DISTRIBUTING>
	groups: lagg
	media: Ethernet autoselect
	status: active
	nd6 options=29<PERFORMNUD,IFDISABLED,AUTO_LINKLOCAL>
1 Esta interface de red, es la que tengo lagg0 con LAG que me permite unir dos interfaces de red.

Borrando Jails de AppJail

Si queremos borrar los jails hacemos los siguiente

  • Paramos todas la Jaulas

  • chflags -R 0 /usr/local/appjail en un pasado tuvimos un detalle con esto a la hora de borrar un directorio inmutable.

  • rm -rf /usr/local/appjail

También podemos borrar una jaula específica con

appjail jail destroy <jaula>

MakeJail de Nextcloud

nextcloud logo icon

Inicialmente intentamos usar SQLite, pero no pudimos, daba errores raros y tampoco permitío hacer login, muy raro la verdad.

Para facilitar las cosas podemos automatizar y crear un directorio con 5 ficheros, ejemplo:

  • template.conf

  • create-volumes.sh nos permite crear directorios que persistan en el host, y ellos seran compartidos dentro del jail por medio del fstab.

  • Makejail

  • mariadb.sh este es el primero que vamos a ejecutar

  • nextcloud.sh será el otro script a ejecutar luego de tener mariadb lista.

El template.conf

exec.start: "/bin/sh /etc/rc"
exec.stop: "/bin/sh /etc/rc.shutdown jail"
sysvshm: new
sysvsem: new
sysvmsg: new
mount.devfs

El create.volumes.sh

#!/bin/sh

VOLUMEDIR=".volumes" (1)
NEXTCLOUD_VOLUME="${VOLUMEDIR}/nextcloud"
MARIADB_VOLUME="${VOLUMEDIR}/mariadb"

#
# nextcloud
#
mkdir -p "${NEXTCLOUD_VOLUME}/apps"
mkdir -p "${NEXTCLOUD_VOLUME}/config"
mkdir -p "${NEXTCLOUD_VOLUME}/data" (2)
mkdir -p "${NEXTCLOUD_VOLUME}/themes"
mkdir -p "${NEXTCLOUD_VOLUME}/done"
mkdir -p "${NEXTCLOUD_VOLUME}/log"

#
# mariadb
#
mkdir -p "${MARIADB_VOLUME}/db"
mkdir -p "${MARIADB_VOLUME}/done"
1 Esta linea hace la magia, nos permite crear un directorio .volumes en la misma ruta donde es ejecutado permitiendo separar los datos persistentes de los que no seran persistentes.
2 Este es un directorio con datos queremos mantenerlo fuera del directorio webroot de nextcloud, siempre es lo mas recomendable, si nuestro jail es borrado, no importara, tendremos nuestros datos aqui

El Makejail

El Makejail con un parecido al Dockerfile pero, funcionan para contrucción y runtime.

INCLUDE gh+AppJail-makejails/nextcloud

CMD mkdir /media/software-electronica (1)
CMD mkdir /media/public
CMD chown -R www:www /media/software-electronica
CMD chown -R www:www /media/public
CMD chmod 775 /media/software-electronica /media/public

SERVICE apache24 restart
1 Nosotros crearemos un directorio llamado software-electronica dentro del directorio media, que apunta al dataset del host por medio del fstab, este directorio media estara en el userland del jail, no en el host.

El script de MariaDB

#!/bin/sh

VOLUMEDIR="$PWD/.volumes"
MARIADB_VOLUME="${VOLUMEDIR}/mariadb"

mkdir -p files/usr/local/etc/mysql/conf.d

cat << EOF > files/usr/local/etc/mysql/conf.d/nextcloud.cnf
[mysqld]
transaction_isolation = READ-COMMITTED
binlog_format = ROW
EOF

./create-volumes.sh

appjail makejail \
	-j nextcloud-mariadb \
        -f gh+AppJail-makejails/mariadb \
        -o virtualnet=":<random> address:10.0.0.51 default" \
        -o nat \
        -o copydir="$PWD/files" \
        -o file=/usr/local/etc/mysql/conf.d/nextcloud.cnf \
	-o fstab="${MARIADB_VOLUME}/db mariadb-db <volumefs>" \
	-o fstab="${MARIADB_VOLUME}/done mariadb-done <volumefs>" \
        -- \
        --mariadb_tag "13.2-106" \
        --mariadb_user "xxx" \
        --mariadb_password "xxx" \
        --mariadb_database "xxx" \
        --mariadb_root_password "xxx"

El scrip de nextcloud

#!/bin/sh

VOLUMEDIR="$PWD/.volumes"
NEXTCLOUD_VOLUME="${VOLUMEDIR}/nextcloud"

./create-volumes.sh (1)

appjail makejail \
    -j nextcloud \
    -o virtualnet=":<random> default" \
    -o nat \
    -o expose=8082:80 \ (2)
    -o fstab="${NEXTCLOUD_VOLUME}/apps nextcloud-apps <volumefs>" \
    -o fstab="${NEXTCLOUD_VOLUME}/config nextcloud-config <volumefs>" \
    -o fstab="${NEXTCLOUD_VOLUME}/data nextcloud-data <volumefs>" \ (3)
    -o fstab="${NEXTCLOUD_VOLUME}/themes nextcloud-themes <volumefs>" \
    -o fstab="${NEXTCLOUD_VOLUME}/done nextcloud-done <volumefs>" \
    -o fstab="${NEXTCLOUD_VOLUME}/log nextcloud-log <volumefs>" \
    -o template="$PWD/template.conf" \
    -V NEXTCLOUD_TRUSTED_DOMAINS=192.168.1.250 \ (4)
    -V MYSQL_DATABASE=xxxx \
    -V MYSQL_USER=xxxx \
    -V MYSQL_PASSWORD=xxxx \
    -V MYSQL_HOST=10.0.0.51 \ (5)
    -V NEXTCLOUD_ADMIN_USER=xxxx \
    -V NEXTCLOUD_ADMIN_PASSWORD=xxxx

appjail fstab jail nextcloud set -d /mnt/pool/software-electronica -m /media/software-electronica (6)
appjail fstab jail nextcloud set -d /mnt/pool/public -m /media/public
appjail fstab jail nextcloud set -d /mnt/pool/extensions/bastille/jails/jdownloader/root/media/downloads -m /media/movies-jdownloader
appjail fstab jail nextcloud set -d /mnt/pool/movies -m /media/movies
appjail fstab jail nextcloud compile
appjail fstab jail nextcloud mount -a
1 Importante crear los volúmenes en el directorio actual.
2 El puerto del host será 8082 y el puerto de la jaula será 8082, por ejemplo http://ip_nas:8082
3 Con el fstab permite que el directorio de datos pueda ser usado mientras se está en el host a través del create-volumes.sh
4 Dirección ip para acceder al nextcloud.
5 Dirección ip de la base de datos mariadb.
6 Con el fstab de appjail montamos el dataset dentro de la jail de una vez.

Variables de entorno, comparación con docker

Appjail permite establecer variables de entorno como docker, cosas que @DtxdF supo para su diseño.

Desplegamos primero MariaDB y luego Nextcloud todo debería estar bien y acceder a la UI.

main nextcloud

Habilitando Servidor NTP para el 2FA

Si, sin la hora correcta, al final tendra como resultado el mal funcionamiento del TOTP (Authenticator app) entonces, la generación del código inicial al introducirla en el input de nextcloud no funcionaría.

La notificación siguente de error no es nada útil, y en los logs, no dice que esta pasando.

error time and totp

Cuando configuremos correctamente el servidor NTP en nuestro server, la hora se sincronizará correctamente y el 2FA estará ready

totp enabled

Info

Tunning en el config.php

Este fichero esta en la ruta de

/usr/local/appjail/jails/nextcloud/jail/usr/local/www/nextcloud/config
 'default_phone_region' => 'ES', (1)
 'opcache.enable' => 1,
 'opcache.enable_cli' => 1,
 'opcache.memory_consumption' => 512,
 'opcache.interned_strings_buffer' => 10, (2)
 'opcache.max_accelerated_files' => 10000,
 'opcache.revalidate_freq' => 1,
 'opcache.save_comments' => 1,
1 Esta es una sugerencia de nextcloud en el apartado Overview Security & setup warnings.
2 Esta es otra sugerencia de dicho apartado.

Redis cache

Para habilitar el redis cache, añadimos esta config, puede ser opcional pero preferi hacerla de una vez, de seguro el performance se vera cuando exista una alta cantidad de ficheros.

'memcache.local' => '\\OC\\Memcache\\Redis',
  'memcache.locking' => '\\OC\\Memcache\\Redis',
  'filelocking.enabled' => 'true',
  'redis' =>
  array (
    'host' => '/var/run/redis/redis.sock',
    'port' => 0,
    'timeout' => 0.0,
  ),
Es importante tener redis cache instalada correctamente, porque sino puede afectarnos a la hora de visualizar los documentos en el nextcloud por ejemplo.

Logs de nextcloud

appjail cmd jexec nextcloud cat /var/log/nextcloud/nextcloud.log

O desde la interface web también es util

Usando el occ

“ownCloud Console”

Bastante útil este comando para ejecutarlo en FreeBSD, como prueba vamos a quitar el index.php de las URL’s

'htaccess.RewriteBase' => '/', (1)
1 Añadimos esto en el fichero config.php

Y ejecutamos esto:

appjail cmd jexec nextcloud occ maintenance:update:htaccess (1)
1 Esto actualizará el .htaccess

Antes y después de remover el index.php

Escribimos la url en el navegador, independientemente si borramos cache en él.
Con el index.php en la url

remove index php

Sin el index.php en la url

after remove index php

Sería lo mismo si accedemos remotamente en él.

Acceso remoto

Para el acceso remoto debemos hacer cosas, como tener un DNS dinámico con cualquier proveedor que nos guste.

En el config.php es importante tener esto.
'trusted_domains' =>
array (
    0 => 'localhost',
    1 => '192.168.1.250',
    2 => 'subdomain.domain.com', (1)
),
'overwrite.cli.url' => 'https://subdomain.domain.com', (2)
'overwriteprotocol' => 'https', (3)
1 Importante para el acceso remoto.
2 Esta también será una línea importante.
3 Esto permite acceso desde el cliente nextcloud de Android, y sin que el cliente genere error, pero la conexion local no se podrá.

Para simular que estamos fuera de nuestra red y acceder a un servicio interno nuestro ( nextcloud por ejemplo ), necesitamos un proxy y TOR se me hace cómodo.

Al descomprimir simplemente uso

./start-tor-browser.desktop --register-app (1)
1 Me creea un icono para usarlo en Ubuntu y listo nada complicado.

Social login

La autenticación que conocemos todos, con distintos tipos de apps, suele ser bastante comoda, claramente debemos ajustar un poco. También con el paso anterior para tener acceso remoto, nuestro servidor nginx y config.php debe estar ajustado correctamente para permitir que estas apps funcionen bien.

Con Google

Para habilitar el Google sign in se debe ir a google cloud para hacer la configuración, no debemos pagar nada.

El OAuth consent screen si no lo tenemos, debemos hacerlo y no es nada complicado, pero sí que tiene campos con términos un poco raros, aquí definiremos el nombre de nuestra app.

google oauth consent

Para llegar a esta vista, se supone que tenemos la aplicación creada, y podemos visualizar esto:

google client id client secret

  • Client ID

  • Client secret


Con Twitter

Con las intrucciones de Twitter debemos ir al Developer portal, aún siguen usando el nombre de Twitter, raro que no tenga el nombre de y logo aún 😂

Con Twitter necesitamos habilitar el Oauth 1.0 lo que permitirá autenticarnos con él.

Luego crear nuestra app con los requisitos necesarios, vamos al apartado User authentication settings

twitter auth settings

Siguiendo el apartado número 3, usamos la URL de redirección, muy paracida a la de google, llenamos esos dos campos si o si.

url twitter redirecction required

Justo en esta parte, tenemos lo que es el consumer api key para lograr autenticarnos con una cuenta de twitter, dichos datos los usaremos en el nextcloud

  • API key

  • Secret

consumer api key twitter compleja por el pais

En un inicio pense que debería usar el OAuth 2.0 Client ID and Client Secret pero no, no es eso.

Con Telegram

Sin embargo con telegram fue mucho más fácil.

Quedando así:

social login nextcloud

fstab con AppJail

Se llega a un punto donde necesitamos añadir un directorio existente en nuestro jail.

Con appjail podemos ajustar el fstab de una manera distinta a como se haría normalmente, pero se debe tomar en cuenta que dependiendo si tenemos el jail activo o no, es mejor tener precaución.

Con el jail parado

Podemos hacer

appjail cmd jexec nextcloud chown -R www:www /media/data/admin/software-electronica (1)
1 Creamos el directorio software-electronica
appjail fstab jail nextcloud set -d /mnt/pool/software-electronica -m /media/data/admin/software-electronica

Con el jail activo

appjail fstab jail nextcloud set -d /mnt/pool/software-electronica -m /media/data/admin/software-electronica
appjail fstab jail nextcloud compile
[00:00:00] [ debug ] Compiling fstab #0: /mnt/pool/dir /path/new-data-dir-for-nextcloud nullfs rw 0 0
appjail fstab jail nextcloud mount -a

Eso es para permitir más caracteres que un fstab normal no podría

Al final tenemos esto, el check verde indica que todo esta bien con el path y directorio nuevo dentro del jail.

set external storage dir to host

new path inside files software electronica

Marcando directorio como read-only

readonly external storage good

  • En esos 3 puntos, tenemos 3 opciones.

  • Si habilitamos esto, nos permite compartir desde esta ruta, esto es opcional.

  • Esto marca el directorio en read-only, muy útil, para no borrar nada, de todas maneras tenemos una papelera de reciclaje.

Actualización de permisos en el External Storage

Si tenemos un directorio que utiliza el almacenamiento externo y por alguna razón se han cambiado los permisos, para que se reflejen en la interfaz de usuario correctamente, hay que ejecutar un comando con occ files:scan --all.

  • occ files:scan --all --home-only Esto le permite evitar la exploración de las rutas de almacenamiento externo.

occ files:scan --all -help

Entonces tendríamos esta salida por ejemplo:

root@nextcloud:~ # occ files:scan --all
Starting scan for user 1 out of 4 (admin)
... removed for brevity
+---------+-------+-----+---------+---------+--------+--------------+
| Folders | Files | New | Updated | Removed | Errors | Elapsed time |
+---------+-------+-----+---------+---------+--------+--------------+
| 2545    | 11646 | 0   | 533     | 0       | 7      | 00:05:06     |  (1)
+---------+-------+-----+---------+---------+--------+--------------+
1 Esto indica que la exploración ha finalizado.

Demontar directorio con fstab

Si queremos desmontar una unidad

appjail fstab jail nextcloud umount \
path/directorio-a-desmontar (1)
1 Especificando el nombre del directorio sin el slash / inicial.

Tips respaldos de nextcloud

Siempre es la misma historia así que vemos como reusar un dump, y tener por ahí el password del root.

En realidad cuando construyo la jaula de mariadb, hago un restore con el dump, y luego continúo con la instalación de nextcloud.
DUMP

Dentro del contenedor hacemos el dump de esta manera:

root@mariadb:~ # mariadb-dump -u root -p --all-databases > dump-nextcloud.sql
Enter password:
root@mariadb:~ # ls
.cshrc			.k5login		.mysql_history		.shrc
.history		.login			.profile		dump-nextcloud.sql (1)
1 El dump creado y se llama dump-nextcloud.sql
DROP

DROP de la base de datos que creamos en un inicio, o sea nextcloud

root@mariadb:~ # mariadb -h 127.0.0.1 -u root -p nextcloud -e "DROP DATABASE nextcloud"
CREATE

CREATE

root@mariadb:~ # mariadb -h 127.0.0.1 -u root -p -e "CREATE DATABASE nextcloud"
RESTORE

RESTORE

root@mariadb:~ # mariadb -u root -p nextcloud < dump-nextcloud.sql
SHOW VERSION
nextcloud@127.0.0.1 [(none)]> SELECT VERSION()
-> ;
+-----------------+
| VERSION()       |
+-----------------+
| 10.6.16-MariaDB | (1)
+-----------------+
1 row in set (0.000 sec)
1 Viendo la versión que tenemos, por si acaso en un futuro, pero no en este caso.

issue con TOTP

Si tenemos error con el 2FA de TOTP de momento me imagino creando un usario nuevo como admin podria restaurar el QR, sino borrarlo y empezar de nuevo.