El tema central será Jellyfin, pero se necesita tocar varios puntos primero.
A tomar en cuenta.
|
La idea de usar miniDLNA se queda muy muy corta
|
Resulta que freeBSD
tiene algo muy potente, siendo nativo de el llamado jails (prisiones, cárcel) para contenerizar nuestras aplicaciones, muy parecido a docker
, pero más maduro, dando un método de contenerización lijera, a veces llamado virtualización a nivel de OS(Sistema operativo), un jail normalmente contiene un sistema operativo completo en una zona de usuario que corre sobre el Sistema FreeBSD.
Los jails, no tienen su propio kernel, sino, que corren en una porción del kernel del host, el host puede controlar el jail, por medio de ciertos comandos sin entrar en el jail, o correr procesos dentro del jail si se prefiere.
La cuenta root del jail puede controlar todo dentro del jail, pero no fuera de el, cada uno de ellos posee una dirección ip dedicada.
Procesos del Jail o JID
xigmanas: ~# jls (1)
JID IP Address Hostname Path
1 adguardhome /mnt/pool/extensions/bastille/jails/adguardhome/root
2 192.168.1.248 jdownloader /mnt/pool/extensions/bastille/jails/jdownloader/root
4 nextcloud /mnt/pool/extensions/bastille/jails/nextcloud/root
5 reverse-proxy /mnt/pool/extensions/bastille/jails/reverse-proxy/root
6 jellyfin /mnt/pool/extensions/bastille/jails/jellyfin/root
1 | Con el comando jls visualizamos nuestros jails, el JID es como un process ID, cada JID cambia una vez que el jail inicie. |
Usando Bastille
Bastille para administrar nuestros jails con simples comandos, este lo instale desde la interface del xigmanas, no me toco probar desde la consola, pero si fue necesario tener primero a OBI(one button installer). para instalarlo desde ese plugin.
Subcommands
La lista de comandos de bastille es un poco variada subcommands. |
Podemos instalar todo nuestros jails via web o por consola
Jails.conf, fstab
Cada Jail tiene 2 ficheros importantes, por ejemplo:
-
jail.conf
-
fstab
Más info al respecto jail-config |
Las rutas de ambos ficheros, estan el dataset donde esta bastille, en mi caso llamado pool
/mnt/pool/extensions/bastille/jails/jdownloader/jail.conf
/mnt/pool/extensions/bastille/jails/jdownloader/fstab
Si queremos entrar dentro de un jail usariamos el siguiente comando.
bastille console Jdownloader (1)
1 | comando que permite entrar al jail llamado Jdownloader |
Dirección ip del jail
Si queremos asignar una dirección ip a nuestro jail bien sea estatica o dinámica, nos vamos al etc/rc.conf
Donde tenemos la ruta siguiente de dicho fichero
/mnt/pool/extensions/bastille/jails/jellyfin-clone/root/etc/rc.conf
Una de las dos líneas se usara, estática o dinámica. |
syslogd_flags="-ss"
sendmail_enable="NO"
sendmail_submit_enable="NO"
sendmail_outbound_enable="NO"
sendmail_msp_queue_enable="NO"
cron_flags="-J 60"
ifconfig_e0b_bastille4_name="vnet0"
ifconfig_vnet0="inet 192.168.1.247/24" (1)
#ifconfig_vnet0="SYNCDHCP" (2)
defaultrouter="192.168.1.1"
jellyfinserver_enable="TRUE"
jellyfinserver_user="jellyfin"
jellyfinserver_group="jellyfin"
1 | Se asigna a este jail una dirección ip estática dentro del pool DHCP de mi red local, con subnet /24 . |
2 | Se usa SYNCDHCP y esto asignará una dirección ip dinámica, justo como lo comenta aquí el código fuente |
El código del enlace anterior.
# If 0.0.0.0 set DHCP, else set static IP address
if [ "${IP}" == "0.0.0.0" ]; then
sysrc -f "${bastille_jail_rc_conf}" ifconfig_vnet0="SYNCDHCP"
else
sysrc -f "${bastille_jail_rc_conf}" ifconfig_vnet0="inet ${IP}"
fi
Bug al clonar jail con bastille
El bug no es nada complejo de resolver, solo que se debe fixear la vnet correctamente, solo cambiar y ya, porque cuando clona, copia la interface del jail original sin escribir la nueva interface del jail clonado entonces:
En nuestro jails.conf
jellyfin-clone {
devfs_ruleset = 13;
enforce_statfs = 2;
exec.clean;
exec.consolelog = /mnt/pool/extensions/bastille/logs/jellyfin-clone_console.log;
exec.start = '/bin/sh /etc/rc';
exec.stop = '/bin/sh /etc/rc.shutdown';
host.hostname = jellyfin-clone;
mount.devfs;
mount.fstab = /mnt/pool/extensions/bastille/jails/jellyfin-clone/fstab;
path = /mnt/pool/extensions/bastille/jails/jellyfin-clone/root;
securelevel = 2;
vnet;
vnet.interface = e0b_bastille5; (1)
exec.prestart += "jib addm bastille5 igb0"; (2)
exec.prestart += "ifconfig e0a_bastille5 description \"vnet host interface for Bastille jail jellyfin-clone\""; (3)
exec.poststop += "jib destroy bastille5"; (4)
allow.mlock;
allow.raw_sockets;
}
1 | Nuestra inteface es e0b_bastille5, y debe estar también en las siguentes líneas. |
2 | Esta línea la cambie porque antes estaba la interface bastille1 del jail original |
3 | Se usa bastille5 |
4 | Se usa bastille5 también. |
Jail no arranca?
Al clonar, por lo visto el fstab toca actualizar los nombres correctos al nuevo nombre del jail. |
xigmanas: ~# bastille start jellyfin-clone (1)
[jellyfin-clone]:
(2)
mount_nullfs: /mnt/pool/extensions/bastille/jails/jellyfin/root/mnt/series: Resource deadlock avoided
jail: jellyfin-clone: /sbin/mount -t nullfs -o rw /mnt/pool_2/series /mnt/pool/extensions/bastille/jails/jellyfin/root/mnt/series: failed
xigmanas: ~# bastille start jellyfin-clone (3)
[jellyfin-clone]:
e0a_bastille5
e0b_bastille5
jellyfin-clone: created
xigmanas: ~# bastille console jellyfin-clone
[jellyfin-clone]:
root@jellyfin-clone:~ # ping google.com
PING google.com (142.250.201.78): 56 data bytes
64 bytes from 142.250.201.78: icmp_seq=0 ttl=116 time=5.877 ms
64 bytes from 142.250.201.78: icmp_seq=1 ttl=116 time=8.838 ms
64 bytes from 142.250.201.78: icmp_seq=2 ttl=116 time=8.711 ms
^C
--- google.com ping statistics ---
3 packets transmitted, 3 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 5.877/7.809/8.838/1.367 ms
1 | Iniciamos nuestro jail y vemos que en la consola arroja un error justo en el fstab. |
2 | El jail clonado tiene un nombre incorrecto apuntando al original jellyfin, entonces lo cambiamos al nuevo nombre que es jellyfin-clone y listo |
3 | Luego de correjir el fstab todo bien. |
Problemas al borrar/eliminar un Jail 🔨
Muchas veces por alguna razón y los permisos del usuario root
impiden borrar el directorio donde el jail se ha instalado, bien sea por la flag schg
que esta presente en dicho directorio, o fichero, haciendolos inmutables (anti borrable entre comillas), para eso hay un comando útil por ejemplo:
chflags -R 0 borrarDirectorio (1)
rm -rf borrarDirectorio (2)
1 | Esto remueve la flag schg y con -R de manera recursiva dentro del dir. |
2 | Luego podemos borrar el dir con facilidad. |
Con los pasos anteriores bastaría para borrar el directorio y solucionar el problema.
Pero tenemos otro escenario, como estamos trabajando en un sistema de ficheros zfs, se incluyo en el código fuente de bastille el parametro -f
para usarlo junto con destroy
servernas: jails# rm -rf mongodb
rm: mongodb/root: Device busy (1)
rm: mongodb: Device busy
1 | No se puede borrar, según esta ocupada la unidad |
servernas: /# cd /mnt/pool/extensions/bastille/jails
servernas: jails# ls
adguardhome apps-java jdownloader jellyfin mongodb reverse-proxy
servernas: jails# bastille destroy -f mongodb (1)
Deleting Jail: mongodb.
servernas: jails# ls (2)
adguardhome apps-java jdownloader jellyfin reverse-proxy
1 | Esto nos permite también poder borrar el jail anti-borrable 🤣. |
2 | Ya no existe el jail dentro del dataset que era mongodb, porque lo borramos. |
Información al respecto. |
Instalando Jdownloader
Desde hace muchos años, uso Jdownloader y de verdad que siempre me vino bien para reconexión automática, reanudar/continuar las descargas, descompresión automática, conectarme a otros servidores como mega etc…
Instalar jdk
Viene bien la openjdk11
xigmanas: ~# bastille console jdownloader (1)
root@jdownloader:~ # pkg install openjdk11 (2)
1 | Entramos al jail jdownloader |
2 | Instalamos la open jdk. |
root@jdownloader:~ # java -version (1)
openjdk version "11.0.12" 2021-07-20
OpenJDK Runtime Environment (build 11.0.12+7-1)
OpenJDK 64-Bit Server VM (build 11.0.12+7-1, mixed mode)
1 | Mostramos la versión de java actual. |
Descargar megatools e iniciar Jdownloader
Megatools y url del .jar de Jdownloader
|
root@jdownloader:~ # pkg install megatools (1)
root@jdownloader:~/java # megadl https://mega.nz/file/2EkgUSga#Pjau9db2bBDES-ih4iWYlHfwC0t-444eFfm0SQegqRA (2)
Downloaded JDownloader.jar
root@jdownloader:~/java # ls -la
total 4134
drwxr-xr-x 2 root wheel 3 Jul 4 16:32 .
drwxr-x--- 19 root wheel 37 Jul 4 16:32 ..
-rw-r--r-- 1 root wheel 4411735 Jul 4 16:32 JDownloader.jar
1 | Instalamos megatools |
2 | Comando megadl para descargar desde mega |
java -jar JDownloader.jar -norestart (1)
1 | Este comando permite iniciar Jdownloader pero sin interface grafica, es decir modo consola, y muchas veces, requiere ejecutarlo nuevamente si nos sale la imagen de abajo. |
Al final para conectarnos a el, lo haríamos mediante MyJDownloader este existe en versión app para Android, IOs no lo se, y un plugin para Google Chrome MyJDownloader Browser Extension
Jdownloader con autoboot
Este proceso de autoboot, podemos hacerlo con otra aplicación, asi como hace git automáticamente que también crea su propio daemon, con adguardhome aplique el mismo procedimiento. |
El paso anterior con el parametro -norestart
, no merece la pena por, dado que a veces, el jdownloader con alguna actualización pendiente requiere que se ejecute de nuevo.
Además si nuestro NAS lo apagamos, nuestro jail no iniciara al Jdownloader por lo comentado anteriormente, entonces, en nuestro jail de Jdownloader nos creamos un pequeño fichero de configuración.
Creando fichero en etc/rc.d
El contenido del fichero que permitirá que nuestro Jdownloader arranque en cada inicio es este:
root@jdownloader:/etc/rc.d # touch jdownloader (1)
root@jdownloader:/etc/rc.d # chmod +x jdownloader (2)
root@jdownloader:/etc/rc.d # nano jdownloader (3)
1 | Creamos un fichero llamado jdownloader en esa ruta |
2 | Establecemos permisos de ejecución |
3 | Editamos con nano o el que sea 😆 |
#!/bin/sh
. /etc/rc.subr
name=jd2
rcvar=jd2_enable (1)
start_cmd="${name}_start"
stop_cmd="${name}_stop"
load_rc_config $name
jd2_start()
{
#!/bin/bash
echo "starting JDownloader2..."
umask 000
cd /jdownloader/
/usr/local/bin/java -Djava.awt.headless=true -jar /root/jdownloader/JDownloader.jar >/dev/null 2>/dev/null & (2)
}
jd2_stop()
{
#!/bin/bash
echo "killing java (and JDownloader2)..."
pgrep java | xargs kill
}
jd2_restart()
{
#!/bin/bash
echo "killing java (and JDownloader2)..."
pgrep java | xargs kill
umask 000
cd /jdownloader/
echo "starting JDownloader2..."
/usr/local/bin/java -Djava.awt.headless=true -jar /root/jdownloader/JDownloader.jar >/dev/null 2>/dev/null &
}
run_rc_command "$1"
1 | Es el nombre de nuestro daemon j2d podemos llamarle como queramos |
2 | Esto inicia nuestro .jar y crea un PID del proceso/daemon |
Ruta /etc/rc.conf
Necesitamos ahora ir al /etc/rc.conf de nuestro jail y añadir la siguiente líneas, para habilitar nuestro daemon de JDownloader en cada inicio del jail.
syslogd_flags="-ss"
sendmail_enable="NO"
sendmail_submit_enable="NO"
sendmail_outbound_enable="NO"
sendmail_msp_queue_enable="NO"
cron_flags="-J 60"
ifconfig_e0b_bastille4_name="vnet0"
ifconfig_vnet0="inet 192.168.1.248/24"
defaultrouter="192.168.1.1"
jd2_enable="YES" (1)
1 | Habilitando el daemon en el boot del jail |
Es importante ejecutar nuevamente el .jar para añadir nuestra autenticación con email y password como en la imagen de abajo. |
Verificando actualización de JDownloader
63|Log.L.log 12/2/23, 2:57:50 PM - INFO [ org.appwork.storage.JsonKeyValueStorage(<init>) ] -> Prefer (merged) JSon Storage from File: /root/jdownloader/cfg/org.jdownloader.captcha.v2.solver.solver9kw.Captcha9kwSettings.json
63|Log.L.log 12/2/23, 2:57:50 PM - INFO [ org.appwork.loggingv3.LogV3(info) ] -> Overridden Config Key found public abstract boolean org.jdownloader.captcha.v2.solver.solver9kw.Captcha9kwSettings.isEnabled()<-->public abstract boolean org.jdownloader.captcha.v2.ChallengeSolverConfig.isEnabled()
Start Update
Update Message: Check for updates
Update Progress: 3%
Update Progress: -1%
Update Message: Contact Server...
Update Message: Check for updates
Update Progress: 100% (1)
1 | Actualización lista. |
Ahora mismo podriamos ir a la web https://my.jdownloader.org/
y loguearnos.
En caso de reiniciar el jail por nuestra parte bastaría con un ejecutar jexec o bastille cmd que es una utilidad para ejecutar comandos dentro de un jail, sin estár dentro de el.
xigmanas: /# bastille restart jdownloader (1)
[jdownloader]:
jdownloader: removed
[jdownloader]:
e0a_bastille4
e0b_bastille4
jdownloader: created
xigmanas: /# jexec -l jdownloader top (2)
last pid: 16487; load averages: 0.16, 0.10, 0.04
4 processes: 1 running, 3 sleeping
CPU: 0.3% user, 0.0% nice, 0.0% system, 0.0% interrupt, 99.7% idle
Mem: 295M Active, 962M Inact, 8604M Wired, 21G Free
ARC: 5636M Total, 566M MFU, 5007M MRU, 96K Anon, 17M Header, 47M Other
4611M Compressed, 5333M Uncompressed, 1.16:1 Ratio
Swap: 2048M Total, 2048M Free
PID USERNAME THR PRI NICE SIZE RES STATE C TIME WCPU COMMAND
16135 root 50 52 0 10G 316M uwait 21 0:24 2.40% java (3)
16487 root 1 20 0 14M 3580K CPU4 4 0:00 0.05% top
16411 root 1 20 0 13M 2772K select 15 0:00 0.00% syslogd
16459 root 1 20 0 13M 2604K nanslp 10 0:00 0.00% cron
1 | reiniciando el jail y todo bien |
2 | Mirando que el proceso java dentro del jail este activo |
3 | daemon de jdownloader activo 🔥 |
xigmanas: ~# bastille cmd jdownloader top
[jdownloader]:
last pid: 56994; load averages: 0.00, 0.00, 0.00 up 26+10:35:47 19:30:08
6 processes: 1 running, 5 sleeping
CPU: 0.0% user, 0.0% nice, 0.1% system, 0.0% interrupt, 99.9% idle
Mem: 143M Active, 12G Inact, 8208K Laundry, 18G Wired, 1243M Free
ARC: 16G Total, 1615M MFU, 14G MRU, 96K Anon, 120M Header, 299M Other
14G Compressed, 16G Uncompressed, 1.13:1 Ratio
Swap: 2048M Total, 2048M Free
PID USERNAME THR PRI NICE SIZE RES STATE C TIME WCPU COMMAND
5670 root 53 52 0 11G 745M uwait 11 54:58 0.15% java (1)
56974 root 1 20 0 14M 3700K CPU7 7 0:00 0.01% top
5996 root 1 20 0 13M 2580K nanslp 20 0:07 0.00% cron
5951 root 1 20 0 13M 2772K select 17 0:05 0.00% syslogd
56975 root 1 20 0 13M 2584K piperd 7 0:00 0.00% cron
56976 root 1 20 0 13M 2584K nanslp 23 0:00 0.00% cron
1 | proceso java activo. |
Template de jdownloader
Con el parametro template usamos bajo fondo un bastillefile que nos permitira automatizar procesos luego de crear un jail, muy util tal cual como hace un dockerfile.
El fichero bastillefile debe ir dentro del directorio de bastille/template
bastille template jdownloader test/bastille-jdownloader (1)
1 | Aplicamos el template al jail corriendo, llamado jdownloader. |
Tenemos también 2 parametros opcionales, para cambiar la versión de la jdk y la de jdownloader que casi siempre actualizan el enlace de mega.
-
OPEN_JDK_VERSION
-
J2_URL
bastille template jdownloader test/bastille-jdownloader --arg OPEN_JDK_VERSION=17
ARG OPEN_JDK_VERSION=11
ARG J2_URL=https://mega.nz/file/fRNwxBza#oTUD5FEuWccnIvHzPdMwdE_ju4HndEDXbaf-VtoBnao
PKG openjdk${OPEN_JDK_VERSION} megatools
CMD mkdir jdownloader
CMD chmod -R 775 jdownloader
CMD cd jdownloader && megadl ${J2_URL}
CMD chmod +x jdownloader/JDownloader.jar
#Copy the etc file from the template to the jdownloader jail etc file
CP etc /
RENDER /etc/rc.d/jdownloader
CMD chmod +x /etc/rc.d/jdownloader
SYSRC jd2_enable="YES"
CONFIG set allow.mlock;
CONFIG set allow.raw_sockets;
CMD java -jar jdownloader/JDownloader.jar
CMD pkill java
CMD java -jar jdownloader/JDownloader.jar -norestart (1)
1 | El último comando en la cual lanzara un login a Jdownloader, e introduciremos nuestro user y password. |
Actualizar desde MyJDownloader
En la interface web, en settings podemos actualizarle, pero como tenemos autoboot él se reiniciará automáticamente.
Esta actualización la realize por gusto, en realidad es opcional, sin hacerla me permitía usar jdownloader tranquilamente. |
Ruta de descarga
Aquí en settings también podemos cambiar la ruta de descarga, el directorio debe existir en nuestro jail
Instalando Jellyfin
Esto no es , nosotros nos corresponde hacer todo el trabajo de guardar las películas en nuestro NAS, y desde el Jellyfin, acceder a ese directorio, pool/dataset donde las tengamos, para poder verlas 😁
Jellyfin es un fork de emby, como plex, pero opensource, como muchas opciones para tener nuestras películas en nuestro NAS organizadas, editar subtítulos, control de usuarios hasta control parental, añadir musica, tiene un Api para aplicaciones externas, extensiones, la versión que usare es 10.7.7
es un poco vieja, pero viene bastante bien, muy superior al miniDLNA 😅.
Pasos básicos de instalación más repositorio de inspiración
|
Configuración del jails.conf
Para jellyfin es importante añadir las últimas 2 líneas:
jellyfin {
devfs_ruleset = 13;
enforce_statfs = 2;
exec.clean;
exec.consolelog = /mnt/pool/extensions/bastille/logs/jellyfin_console.log;
exec.start = '/bin/sh /etc/rc';
exec.stop = '/bin/sh /etc/rc.shutdown';
host.hostname = jellyfin;
mount.devfs;
mount.fstab = /mnt/pool/extensions/bastille/jails/jellyfin/fstab;
path = /mnt/pool/extensions/bastille/jails/jellyfin/root;
securelevel = 2;
vnet;
vnet.interface = e0b_bastille0;
exec.prestart += "jib addm bastille0 igb0";
exec.prestart += "ifconfig e0a_bastille0 description \"vnet host interface for Bastille jail jellyfin\"";
exec.poststop += "jib destroy bastille0";
allow.mlock; (1)
allow.raw_sockets; (2)
}
1 | Relacionado con la memoria del jail. |
2 | Habilita utilidades como ping y traceroute dentro del jail. |
La dirección ip a usar es la que le asignamos al jail y puerto por defecto 8096 |
Error al reinstalar Jellyfin
Me paso muchas veces que al querer reinstalar jellyfin, no podía hacerlo por tener un UID repetido, en realidad por usuarios/grupos.
|
The service file uses daemon to restart jellyfinserver if it crashes.
The service file will also change the permissions so that the updater works.
If this behavior is unwanted you will need to edit the RC file manually and
remove the daemon and/or the permissions changes.
If you are running this in a jail please set "allow_mlock=1" or similar
for this jail otherwise the program will fail to start.
dotNET does not work well inside jails that are missing either a) VNET or
one of b) ip6=inherit c) ip6=new. The service file will try workaround any
user misconfiguration but is not perfect.
root@jailvnet2:~ # pw user add jellyfin -c jellyfin -u 710 -d /nonexistent -s /usr/bin/nologin
pw: uid `710' has already been allocated (1)
root@jailvnet2:~ # ln -s /usr/local/lib/libsqlite3.so /usr/local/lib/libe_sqlite3
root@jailvnet2:~ # sysrc jellyfinserver_enable=TRUE
jellyfinserver_enable: -> TRUE
root@jailvnet2:~ # sysrc jellyfinserver_user=jellyfin
jellyfinserver_user: -> jellyfin
root@jailvnet2:~ # sysrc jellyfinserver_group=jellyfin
jellyfinserver_group: -> jellyfin
root@jailvnet2:~ # service jellyfinserver start
install: unknown group jellyfin
install: unknown group jellyfin
Starting jellyfinserver.
su: unknown login: jellyfin
/usr/local/etc/rc.d/jellyfinserver: WARNING: failed to start jellyfinserver (2)
root@jailvnet2:~ #
1 | El UID 710 estaba repetido, pareciendo que otro usuario lo tiene, entonces lo que hariamos es colocar otro. |
2 | Al intentar iniciar el servidor jellyfin tenemos este error. |
pw user add jellyfin -c jellyfin -u 715 -d /nonexistent -s /usr/bin/nologin (1)
1 | Usamos ahora UID 715, para fixear el error. |
root@jailvnet2:~ # service jellyfinserver start
Starting jellyfinserver. (1)
1 | Con este solo mensaje, indica que todo esta bien, ahora podemos acceder con la dirección ip del jail mas el puerto 8096 |
Actualizando Jellyfin
Es buena idea tener un backup o clonar el jail en casos como estos, bastille lo permite, vía consola o web. |
root@jellyfin:~ # service jellyfinserver stop (1)
Stopping jellyfinserver.
root@jellyfin:~ # fetch https://github.com/Thefrank/jellyfin-server-freebsd/releases/download/v10.8.1/jellyfinserver-10.8.1.pkg (2)
jellyfinserver-10.8.1.pkg 58 MB 7305 kBps 08s
root@jellyfin:~ # ls
.cshrc .k5login .shrc
.history .login jellyfinserver-10.7.7.pkg
.hushlogin .profile jellyfinserver-10.8.1.pkg
root@jellyfin:~ # pkg install jellyfinserver-10.8.1.pkg (3)
Updating FreeBSD repository catalogue...
[jellyfin] Fetching packagesite.pkg: 100% 6 MiB 3.3MB/s 00:02
Processing entries: 100%
FreeBSD repository update completed. 31614 packages processed.
All repositories are up to date.
New version of pkg detected; it needs to be installed first.
The following 1 package(s) will be affected (of 0 checked):
Installed packages to be UPGRADED:
pkg: 1.17.5 -> 1.18.3 (4)
Number of packages to be upgraded: 1
8 MiB to be downloaded.
Proceed with this action? [y/N]: y (5)
[jellyfin] [1/1] Fetching pkg-1.18.3.pkg: 100% 8 MiB 2.2MB/s 00:04
Checking integrity... done (0 conflicting)
[jellyfin] [1/1] Upgrading pkg from 1.17.5 to 1.18.3...
[jellyfin] [1/1] Extracting pkg-1.18.3: 100%
Updating FreeBSD repository catalogue...
FreeBSD repository is up to date.
All repositories are up to date.
The following 2 package(s) will be affected (of 0 checked):
New packages to be INSTALLED:
jellyfinserver: 10.8.1
krb5-120: 1.20
Number of packages to be installed: 2
The process will require 135 MiB more space.
1 MiB to be downloaded.
Proceed with this action? [y/N]: y (6)
[jellyfin] [1/2] Fetching krb5-120-1.20.pkg: 100% 1 MiB 1.2MB/s 00:01
Checking integrity... done (0 conflicting)
[jellyfin] [2/2] Installing jellyfinserver-10.8.1...
===> Creating groups.
Using existing group 'jellyfinserver'.
===> Creating users
Using existing user 'jellyfinserver'.
[jellyfin] Extracting jellyfinserver-10.8.1: 100%
[jellyfin] [1/2] Installing krb5-120-1.20...
[jellyfin] [1/2] Extracting krb5-120-1.20: 100%
=====
Message from jellyfinserver-10.8.1:
--
jellyfinserver relies on Microsoft dotNET5+ SDK to be built
Microsoft does not have an official version of dotNET for FreeBSD
This package was built with an UNOFFICIAL UNSUPPORTED version of dotNET
If this is something that you do not want, remove this package with
"pkg remove jellyfinserver"
This package installs a service file.
Enable it with "sysrc jellyfinserver_enable=TRUE"
Start it with "service jellyfinserver start".
The service file uses daemon to restart jellyfinserver if it crashes.
The service file will also change the permissions so that the updater works.
If this behavior is unwanted you will need to edit the RC file manually and
remove the daemon and/or the permissions changes.
If you are running this in a jail please set "allow_mlock=1" or similar
for this jail otherwise the program will fail to start.
dotNET does not work well inside jails that are missing either a) VNET or
b) ip6=inherit. The service file will try workaround any user misconfiguration
but is not perfect.
root@jellyfin:~ # service jellyfinserver start (7)
Starting jellyfinserver.
1 | Paramos el servidor jellyfin |
2 | fetch a la nueva versión de jellyfin |
3 | Se instala el .pkg versión 10.8.1 de jellyfin |
4 | Se muestra la versión que se actualizara. |
5 | Yes |
6 | Yes |
7 | Iniciamos el servidor jellyfin |
Tenemos la actualización lista
Registros / Logs
Esta parte nos permite visualizar nuestros logs, muy util.
Puede suceder, que las imagenes de las películas no sean las correctas, pero tampoco puedan cambiarse correctamente desde la interface web, pero puede ser falta de permisos de escritura en el NAS gracias a los logs se puede verificar eso. |
[2022-05-08 15:57:59.602 +00:00] [ERR] [18] MediaBrowser.Providers.Manager.ProviderManager: UnauthorizedAccessException - Access to path "/mnt/movies_1/Spiderman un nuevo universo (2018)/folder.jpg" is denied. Will retry saving to "/var/db/jellyfinserver/metadata/library/a4/a45b8287c09ccc439897b6158d8d88bd/poster.jpg"
[2022-05-08 15:57:59.828 +00:00] [ERR] [18] MediaBrowser.Providers.Manager.ProviderManager: UnauthorizedAccessException - Access to path "/mnt/movies_1/Spiderman un nuevo universo (2018)/backdrop.jpg" is denied. Will retry saving to "/var/db/jellyfinserver/metadata/library/a4/a45b8287c09ccc439897b6158d8d88bd/backdrop.jpg"
[2022-05-08 15:58:02.249 +00:00] [ERR] [24] MediaBrowser.Providers.Manager.ProviderManager: Error in metadata saver
(1)
System.UnauthorizedAccessException: Access to the path '/mnt/movies_1/Spiderman un nuevo universo (2018)/Spider-Man Un nuevo universo (2018).nfo' is denied.
---> System.IO.IOException: Permission denied
--- End of inner exception stack trace ---
at Interop.ThrowExceptionForIoErrno(ErrorInfo errorInfo, String path, Boolean isDirectory, Func`2 errorRewriter)
at Microsoft.Win32.SafeHandles.SafeFileHandle.Open(String path, OpenFlags flags, Int32 mode)
at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options)
at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share)
at MediaBrowser.XbmcMetadata.Savers.BaseNfoSaver.SaveToFile(Stream stream, String path)
at MediaBrowser.XbmcMetadata.Savers.BaseNfoSaver.Save(BaseItem item, CancellationToken cancellationToken)
at MediaBrowser.Providers.Manager.ProviderManager.SaveMetadata(BaseItem item, ItemUpdateType updateType, IEnumerable`1 savers)
1 | Error a la hora de escribir en el directorio, entonces, no se podrian editar las portadas y demás. |
Las imagenes aún con problemas de permisos podemos cambiarla, pero es fastidioso. |
Ajustes de permisos en el dataset
El dataset que tiene las películas debe tener el usuario jellyfin y grupo asignado también debe coincidir con el usuario jellyfin del contenedor como lo hacemos ? fácil, entramos en el contenedor jellyfin y ejecutamos esto para conocer el id de dicho usuario:
servernas: /mnt# bastille console jellyfin
[jellyfin]:
root@jellyfin:~ # id jellyfin (1)
uid=868(jellyfin) gid=868(jellyfin) groups=868(jellyfin)
root@jellyfin:~ # id 868 (2)
uid=868(jellyfin) gid=868(jellyfin) groups=868(jellyfin)
root@jellyfin:~ #
1 | Buscamos el id del usuario jellyfin dentro del container. |
2 | Lo mísmo pero al contrario buscamos el usuario del id 868 dentro del container. |
Creamos el usuario/grupo con el id del contenedor, para que tengas permisos de escritura lectura, ejecución en este dataset.
Para el dataset tengo esta configuración actual y bien todo funciona correctamente.
Justo en el dataset donde guardo todas las peliculas. |
Recordemos que podemos crear el usuario dentro del contenedor igual al del host con el comando anterior para los permisos. |
pw user add jellyfin -c jellyfin -u 868 \ (1)
-d /nonexistent -s /usr/bin/nologin
1 | Id 868 como el del host. |
Creando Watcher con Java
Si un poco loco, porque se puede usar un cron job, pero desde la UI de XigmaNAS es un dolor de cabeza y por alguna razón no funciona, tampoco se dispara correctamente.
Entonces suele pasar que cuando se añade una película al directorio /mnt/pool/movies
los permisos no son los adecuados y la app jellyfin no puede pushear la portada de la película como debería (aunque si lo hace porque suele escribir en otro directorio donde si tiene permisos, pero no justifica igual) ni escribir el .nfo, entonces simplemente lo que se desea hacer es que, si se añaden datos al directorio se seteen los permisos más el usuario correcto y listo sin tanto problema.
Necesitamos:
-
El .jar con el código necesario para actuar como listener de los cambios que se producen en un directorio
-
Acceso root para setear los permisos correctos.
-
El script para añadirlo al arranque, en el rc.d
-
Habilitar el proceso en el inicio en el
/etc/rc.conf
El script del Watcher
#!/bin/sh
. /etc/rc.subr
name=watcher
rcvar=watcher_enable
start_cmd="${name}_start"
stop_cmd="${name}_stop"
load_rc_config $name
: ${watcher_args_movies_pool="-u jellyfin -p /media/downloads/movies -o 775"} (1)
: ${watcher_args_series_pool_3="-u jellyfin -p /media/downloads/series_pool_3 -o 775"}
: ${watcher_args_movies_pool_3="-u jellyfin -p /media/downloads/movies_pool_3 -o 775"}
watcher_start()
{
#!/bin/bash
echo "starting Watcher..."
umask 000 (2)
/usr/local/bin/java -Djava.awt.headless=true -jar /root/watcher/watcher.jar ${watcher_args_movies_pool} >/dev/null 2>/dev/null &
/usr/local/bin/java -Djava.awt.headless=true -jar /root/watcher/watcher.jar ${watcher_args_series_pool_3} >/dev/null 2>/dev/null &
/usr/local/bin/java -Djava.awt.headless=true -jar /root/watcher/watcher.jar ${watcher_args_movies_pool_3} >/dev/null 2>/dev/null &
}
watcher_stop()
{
#!/bin/bash
echo "killing java (and Watch)..."
pgrep java | xargs kill
}
watcher_restart()
{
#!/bin/bash
echo "killing java (and Watcher)..."
pgrep java | xargs kill
umask 000
echo "starting watcher..."
/usr/local/bin/java -Djava.awt.headless=true -jar /root/watcher/watcher.jar ${watcher_args_movies_pool} >/dev/null 2>/dev/null &
/usr/local/bin/java -Djava.awt.headless=true -jar /root/watcher/watcher.jar ${watcher_args_series_pool_3} >/dev/null 2>/dev/null &
/usr/local/bin/java -Djava.awt.headless=true -jar /root/watcher/watcher.jar ${watcher_args_movies_pool_3} >/dev/null 2>/dev/null &
}
run_rc_command "$1"
1 | Estos son los parámetros que se le pasan al watcher
|
2 | Importante, nuestro .jar debe estar en el directorio /root/nombre-cualquier-directorio/watcher.jar en este caso
estamos ejecutando este .jar en un jail. |
El código Java
Ya el código existe en gran parte, pero debemos adaptarlo a nuestro caso, porque dicho código solo escanea cambios en el directorio para ficheros de texto solamente, nosotros vamos más que eso.
La ventaja es que, con cron tenemos un tiempo definido, 2, 3 minutos o el que sea, pero si no queremos esperar 2 minutos o 3 ?, y que los cambios de los directorios se den de inmediato, o mejor dicho en un futuro solo si existen cambios ? pues tenemos esta forma, por medio de eventos, este servicio procesa 3 tipos, CREATE , DELETE , y MODIFY
|
// imports removed for brevity
/**
* Listening for changes in the <strong>/mnt/pool/movies</strong> dataset
* <p>
* And if there is a creation event we change the owner to <strong>jellyfin</strong> with the octal mode 775
*/
public class WatchDir {
private static final Logger LOGGER = Logger.getLogger(WatchDir.class.getName());
/**
* Octal 775
*/
private static final String OCTAL_775 = "rwxrwxr-x";
/**
* Octal 770
*/
private static final String OCTAL_770 = "rwxrwx---";
/**
* The dataset path to watch, is used with the <strong>-p</strong> parameter
*/
private final Path path;
/**
* The octal mode, is used with the <strong>-o</strong> parameter
*/
private final String octal;
/**
* The user name to apply in this directory, is used with the <strong>-u</strong> parameter
*/
private final String userName;
public WatchDir(final String userName, final String path, final String octal) {
this.userName = userName;
this.path = Path.of(path);
this.octal = octal;
this.watchDirectory();
}
@SuppressWarnings("unchecked")
private void watchDirectory() {
try {
WatchService watcher = FileSystems.getDefault().newWatchService(); (1)
this.registerAll(path, watcher); (2)
WatchKey key;
LOGGER.info("Listening in..." + path);
for (;;) {
// wait for key to be signaled
try {
key = watcher.take();
} catch (InterruptedException x) {
return;
}
for (WatchEvent<?> event : key.pollEvents()) {
WatchEvent.Kind<?> kind = event.kind();
if (kind == OVERFLOW) {
continue;
}
WatchEvent<Path> ev = (WatchEvent<Path>) event;
Path filename = ev.context();
if (kind == ENTRY_CREATE) { (3)
/*
* Si pasamos el filename solo se procesara el directorio nuevo.
* Si pasamos el path del parametro -p por el filename, procesaremos el directorio completo
*/
LOGGER.info("CREATE: " + filename);
changePermissionsAndOwnerRecursively(path); (4)
}
if (kind == ENTRY_DELETE) {
LOGGER.info("DELETE: " + filename);
changePermissionsAndOwnerRecursively(path);
}
if (kind == ENTRY_MODIFY) { (5)
LOGGER.info("MODIFY: " + filename);
changePermissionsAndOwnerRecursively(path);
}
}
boolean valid = key.reset();
if (!valid) {
break;
}
}
} catch (IOException x) {
LOGGER.severe(x.getMessage());
}
}
/**
* Register the given directory and all its sub-directories with the WatchService.
*/
private void registerAll(final Path start, final WatchService watchService) throws IOException {
// register directory and sub-directories
Files.walkFileTree(start, new SimpleFileVisitor<>() {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
throws IOException {
dir.register(watchService, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
return FileVisitResult.CONTINUE;
}
});
}
/**
* Change permissions recursively
*
* @param pathParam
*/
private void changePermissionsAndOwnerRecursively(Path pathParam) {
try (Stream<Path> stream = Files.walk(pathParam)) { // FOLLOW_LINKS (6)
stream.forEach(path -> {
this.setPermissions(path); (7)
this.changeOwner(path); (8)
});
LOGGER.info("Owner changed correctly to " + this.userName);
LOGGER.info("Permissions changed correctly");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* Set octal permissions
*
* @param path the current dir or a filename
*
*/
private void setPermissions(final Path path) {
try {
final String octalMode = this.octal.equals("775") ? OCTAL_775 : OCTAL_770;
Set<PosixFilePermission> permissions = PosixFilePermissions.fromString(octalMode);
Files.setPosixFilePermissions(path, permissions); (9)
} catch (IOException e) {
LOGGER.severe(e.getMessage());
}
}
/**
* The user to the directory and files is set up, you must have root permissions to run this.
* <p>
* The user must exist, otherwise, a <strong>UserPrincipalNotFoundException</strong>
*
* @param path
* @throws IOException
*/
private void changeOwner(final Path path) {
try {
UserPrincipalLookupService lookupService = path.getFileSystem().getUserPrincipalLookupService();
UserPrincipal userPrincipal = lookupService.lookupPrincipalByName(this.userName);
Files.setOwner(path, userPrincipal); (10)
} catch (IOException ex) {
LOGGER.severe("The user "+ this.userName + " does not exist");
LOGGER.severe("leaving now...");
System.exit(0);
}
}
/**
*
* @param args
*/
public static void main(String... args) {
System.out.println("");
System.out.println("rubn");
System.out.println("https://rubn0x52.com");
if (args.length == 0) {
LOGGER.info("No input parameters present");
System.exit(1);
} else if (("-h".equals(args[0])
|| "-help".equals(args[0])
|| "--help".equals(args[0])
|| "--h".equals(args[0]))) {
LOGGER.info("usage: java -jar watcher.jar -u [--user ex: an existing user] -p [--path ex: path,dir,dataset] -o [--octal mode ex: 775, 770]");
System.exit(1);
} else {
if (args.length > 3 && args.length <=5) {// valid path args[3]
LOGGER.info("Please view usage with --h, -h, -help, --help" );
System.exit(0);
} else if(args.length > 3 && args.length <=6) {
(11)
new WatchDir(args[1].trim(), args[3].trim(), args[5].trim());
} else {
LOGGER.info("Please view usage with --h, -h, -help, --help" );
System.exit(0);
}
}
}
}
1 | El servicio WatchService la magia pura |
2 | Esta línea se le pasan los eventos que queremos procesar recursivamente por los directorios hijos, se usa el método walkFileTree quedando el código mas limpio que usando el método walk . |
3 | Cuando tenemos un evento de tipo CREATE , o sea, un nuevo directorio. |
4 | Aquí esta la logica nuestra, usamos el método walk para hacer un recorrido recursivo por ese directorio. |
5 | Es necesario procesar eventos tipo MODIFY porque se da el caso donde se añada un directorio en otro existente contando como una modificación. |
6 | El método walk que hace la magia también, puede recorrer enlaces simbólicos pero no es nuestro caso, el número 3 es para la profundidad del recorrido, es decir, si añadimos un directorio que contenta más de 3 carpetas, el solo busca hasta la tercera carpeta las demás las ignorará, en un inicio lo dejo, pero ya no, mejor quitarlo y listo. |
7 | Cambiamos permisos al directorio recursivamente. |
8 | Cambiamos el owner del directorio recursivamente también. |
9 | Sete los permisos en modo octal. |
10 | Esto cambia el owner. |
11 | Aquí instanciamos solo si tenemos los 3 parámetros correctos. |
Verificar si el script se ha ejecutado
Para verificar que nuestro .jar se ha iniciado correctamente usamos
root@jdownloader:~ # ps -aux | grep -e jar
root 7211 0.7 0.1 27799236 94264 0 IJ 11:55 0:01.94 /usr/local/openjdk11/bin/java -Djava.awt.headless=true -jar /root/watcher/watcher.jar -u jellyfin -p /me (1)
root 7209 0.2 0.7 28413212 696620 0 IJ 11:55 0:35.96 /usr/local/openjdk11/bin/java -Djava.awt.headless=true -jar /root/jdownloader/JDownloader.jar
root 7591 0.0 0.0 12812 2360 0 S+J 11:56 0:00.00 grep -e jar
1 | Ejecución correcta. |
En caso de inhabilitar el Script podemos hacerlo en el etc/rc.conf y marcarlo a "NO"
|
Usar imdb por defecto para las portadas
Tenemos que ir a administrar la biblioteca
Con esto lograremos que cada portada use por defecto la que esta en imdb una de las más confiables y viejas.
Aquí vemos que las imágenes son la misma, es decir todo bien. |
OpenSubtitles
Este es un plugin para subtítulos muy útil, en caso de tener una película con caracteres raros en las traducciones.
Necesitamos crearnos un usuario para tener acceso al api, dicho usuario lo introduciremos en nuestro server jellyfin
Estatus del api OpenSubtitles
Esta url permite ver el estado del api de OpenSubtitles
con pequeñas metricas.
Buscando subtítulos
En caso de un película vieja, y queramos subtítulos, en la esquina derecha, tenemos 3 puntos verticales, luego en "Editar subtítulos", que nos mostrara la modal siguente:
Por defecto tenemos
-
Subtitulos Latino - Es
-
Pero queremos Inglés por ejemplo
Metadatos con MKVToolNix
Muchos archivos por ahí (.mp4 .mkv etc), tendran metadatos, e incluso llegan a ser un poco molestos.
En el caso de archivos .mkv viene muy bien el MKVToolNix vía GUI, es muy rápido, importamos nuestra película y editamos el metadato que queramos…
Lo mejor es editar el fichero en local, si editamos el video directamente en el directorio del NAS, tardaria muchisimo (aún en LAN), podemos descargar la película desde el mismo jellyfin a nuestra pc editar y luego subirla.
En el menu donde "multiplexador" tenemos varias cosas:
-
Codec
-
Cada item corresponde a una parte que podemos editar.
-
-
Propiedades
-
Podemos cambiar por ejemplo "Nombre de pista".
-
-
Pestaña/tab Salida
-
General → Título del archivo: escribimos el nombre del video(o simplemente borrar el texto original que no queramos), en ciertos reproductores, este nombre se mostrara en el inicio de la película.
-
-
Archivo de salida:
-
Este será el nombre del fichero en realidad puede ser cualquiera, no es un metadato, se puede editar como cualquier archivo.
-
-
Iniciar multiplexado
-
Luego de tener todo listo, clickeamos ese botón.
-
Seteando path del ffmpeg
Hacer esto es obligatorio, para poder reproducir. |
Desde la release de jellyfin 10.8.13, el input del ffmeg fue deshabilitado por seguridad, y debemos setearla nosotros desde la consola
Dentro del jail tenemos el fichero encoding.xml
justo en esta ruta y es el que debemos editar
/root/var/db/jellyfin/config
Con el comando which ffmpeg
obtenemos la ruta usr/local/bin/ffmpeg
Hacemos service jellyfin stop
y editamos con nano encoding.xml
para añadir la ruta anterior.
Editamos
<EncoderAppPath>/usr/local/bin/ffmpeg</EncoderAppPath>
<EncoderAppPathDisplay>/usr/local/bin/ffmpeg</EncoderAppPathDisplay>
Nuevamente iniciamos el servicio con service jellyfin start
y tendriamos ya el input seteado correctamente como aquí
Amazon fire stick 4k
El Amazon fire stick nos permite instalar aplicaciones en el, basado en Android tv, nos quitamos esa molestia que nos pasa con ciertos smart-tv a la hora de instalar una app, evitando usar el protocolo DLNA que es más inestable de lo que parece.
En el se instalará la versión cliente
de jellyfin, muy fácil e intuitivo, lo cuál permitirá acceder al jellyfin instalado en el jail, versión servidor de él, con ip y puerto igual.
Ofrece conector macho HDMI, la mayoría de los smart-tv tienen puerto`s HDMI hembra, son un standard prácticamente hoy en día.
También ideal si lo llevamos a un lugar remoto, lo conectamos y accedemos a nuestro NAS tanto local, o remotamente con ciertos cambios, sería útil tener un dns dinámico como veremos próximamente con duckdns
para esas conexiónes remotas."
Dimensiones | 99 x 30 x 14 mm (solo el dispositivo) 108 x 30 x 14 mm (incluyendo el conector) |
---|---|
Peso |
53,6 |
Procesador |
Quad-Core de 1,7 GHz |
GPU |
IMG GE8300 |
Almacenamiento |
8 GB |
Wifi |
Wifi de doble banda y doble antena (MIMO) que permite una transferencia de datos más rápida y pérdida de conexión menor que con el wifi estándar. Compatible con redes wifi 802.11a/b/g/n/ac. |
Bluetooth |
Bluetooth 5.0 y LE. Se puede vincular con altavoces, auriculares, mandos de videojuegos y otros dispositivos Bluetooth compatibles. |
Reverse proxy para acceso remoto
Ian me trajo recuerdos con una cosa interesante, para mejor administración de los certificados SSL con un server nginx.
Nuevamente nos creamos un Jail para instalar nuestro servidor nginx y redireccionar las peticiones http que queramos que pasen por aquí.
/usr/local/etc/nginx/nginx.conf (1)
1 | Ruta de configuración del proxy nginx |
En caso de editar el fichero nginx.conf
service nginx reload (1)
1 | Para reiniciar el servidor nginx |
Redirección a Https
Por lo visto si accedemos con http a nuestros servidores con nginx por defecto es posible entrar normal, y eso no lo queremos, podemos hacer una redirección a https, pero debemos ajustar nuestro nginx.conf
Fue necesario para esto, abrir el puerto 80 en el router con la ip del reverse-proxy, porque sin eso no funciona. |
server {
listen 443 ssl; (1)
server_name localhost nuestroDominio.duckdns.org;
ssl_certificate /usr/local/etc/letsencrypt/live/dominio/fullchain.pem;
ssl_certificate_key /usr/local/etc/letsencrypt/live/dominio/privkey.pem;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
location / {
proxy_pass http://192.168.1.130:8096; (2)
}
# HSTS (ngx_http_headers_module is required) (63072000 seconds)
add_header Strict-Transport-Security "max-age=63072000 includeSubDomains; preload" always;
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/local/www/nginx-dist;
}
set $https_redirect 0;
#if request came from port 80/http
if ($server_port = 80) {
set $https_redirect 1;
}
# or if the requested host came with www
if ($host ~ '^www\.') {
set $https_redirect 1;
}
#then it will redirects
if ($https_redirect = 1) {
return 301 https://$server_name$request_uri;
}
}
server { (3)
listen 80 default_server;
server_name _;
return 301 https://$host$request_uri;
}
1 | Config para el https |
2 | Nuestro server en local de jellyfin |
3 | Confi necesaria de http |
Como renovar el certificado SSL de letsencrypt con certbot?
Podemos usar certbot con instrucciones para freeBSD los certificados que hemos creado con letsencrypt, estos se vencen a los 90 días.
root@reverse-proxy:/usr/local/etc/nginx # certbot renew (1)
root@reverse-proxy:/usr/local/etc/nginx # certbot renew --dry-run (2)
1 | Para renovar los certificados. |
2 | Sirve para testear la renovación automática, sin generar los certificados. |
Pero obtenemos este error
Saving debug log to /var/log/letsencrypt/letsencrypt.log (1)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Processing /usr/local/etc/letsencrypt/renewal/mydomain.bla.conf
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Renewing an existing certificate for mydomain.bla
Certbot failed to authenticate some domains (authenticator: standalone). The Certificate Authority reported these problems:
Domain: mydomain.bla
Type: unauthorized
Detail: ip: Invalid response from http://mydomain.bla/.well-known/acme-challenge/e5P1zpHbQUyQ6R28jew3Z9rRxpMPzUdsLE7Gdlp5ZqI: 404
(2)
Hint: The Certificate Authority failed to download the challenge files from the temporary standalone webserver started by Certbot on port 80. Ensure that the listed domains point to this machine and that it can accept inbound connections from the internet.
Failed to renew certificate mydomain.bla with error: Some challenges have failed.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
All renewals failed. The following certificates could not be renewed:
/usr/local/etc/letsencrypt/live/mydomain.bla/fullchain.pem (failure)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1 renew failure(s), 0 parse failure(s)
Ask for help or search for solutions at https://community.letsencrypt.org. See the logfile /var/log/letsencrypt/letsencrypt.log or re-run Certbot with -v for more details.
1 | Ruta del log generado, y que debemos inspeccionar en caso de algo raro. |
2 | Por lo visto esta estrechamente relacionado con nuestro error, y el puerto 80 bloqueado |
Si investigamos mas con el comando certbot renew -v
tenemos que se usa el:
http-01 challenge for xxx.xxx.net pero justo este no nos sirve .
|
Necesitamos usar otro challenge
que nos permita hacer esto, nuestro proveedor nos bloquea el puerto 80 😡.
Usando DNS dinámico con DuckDNS
Anteriormente me valia con no-ip para mi ip dinámica, pero con este dns-01-challenge necesito escribir los record TXT
en el, y no me va, porque habría que pagar, prefiero darle ese dinero a los pobres.
Ahora con duckdns me da todo, gratis, y un api-rest para actualizar el record TXT, una vez que introduzca el comando necesario con certbot:
certbot certonly --manual --preferred-challenges dns -d miDominio.duckdns.org (1)
1 | Mi dominio |
root@reverse-proxy:/logs-letsencrypt # certbot certonly --manual --preferred-challenges dns -d foo.duckdns.org
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Certificate not yet due for renewal
You have an existing certificate that has exactly the same domains or certificate name you requested and isn't close to expiry.
(ref: /usr/local/etc/letsencrypt/renewal/foo.duckdns.org.conf)
What would you like to do?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: Keep the existing certificate for now (1)
2: Renew & replace the certificate (may be subject to CA rate limits)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select the appropriate number [1-2] then [enter] (press 'c' to cancel):
1 | La opción (1) me tocará ya que tengo el certificado vigente. |
En caso de tener que renovar el certificado los pasos para actualizar el record TXT es aquí.
root@reverse-proxy:/logs-letsencrypt # certbot certonly --manual --preferred-challenges dns -d mySubName (1)
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Requesting a certificate for bar.duckdns.org
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please deploy a DNS TXT record under the name:
_acme-challenge.bar.duckdns.org.
with the following value:
L9-------------------------------------(2)
Before continuing, verify the TXT record has been deployed. Depending on the DNS
provider, this may take some time, from a few seconds to multiple minutes. You can
check if it has finished deploying with aid of online tools, such as the Google
Admin Toolbox: https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.bar.duckdns.org.
Look for one or more bolded line(s) below the line ';ANSWER'. It should show the
value(s) you've just added.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1 | mySubName ejemplo bar.duckdns.org |
2 | Token generado de 43 caracteres para nuestro record TXT, para usar en los query-parameters del endpoint ofrecido por duck dns, es el paso siguiente. |
Invocando al endpoint para actualizar certificado
Desde la consola usando curl o desde el navegador, o sea, enviar un GET para consumir la URL.
https://www.duckdns.org/update?domains=mySubName&token=myTokenDeDuckDns&txt=tokenGenerado&verbose=true (1)
1 | Insertamos nuestro token de duckdns (el que nos genera la web) en el parametro token, también nuestro token generado en el paso anterior para usarle en el parametro txt |
Cuando el certificado se ha actualizado correctamente
Successfully received certificate.
Certificate is saved at: /usr/local/etc/letsencrypt/live/boo.duckdns.org/fullchain.pem
Key is saved at: /usr/local/etc/letsencrypt/live/foo.duckdns.org/privkey.pem
This certificate expires on 2023-12-06.
These files will be updated when the certificate renews.
NEXT STEPS:
This certificate will not be renewed automatically. Autorenewal of --manual certificates requires the use of an authentication hook script (--manual-auth-hook) but one was not provided. To renew this certificate, repeat this same certbot command before the certificate's expiry date.
If you like Certbot, please consider supporting our work by:
Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate
Donating to EFF: https://eff.org/donate-le
Una vez que setemos la nueva ruta de nuestros certificados fullchain.pem
y privkey.pem
nos bastaría con reiniciar nuestro server nginx
root@reverse-proxy:/servernginx # service nginx reload (1)
Performing sanity check on nginx configuration:
nginx: the configuration file /usr/local/etc/nginx/nginx.conf syntax is ok
nginx: configuration file /usr/local/etc/nginx/nginx.conf test is successful
1 | reiniciando el nginx server. |
Abrir puertos en el router
Toca también abrir los puertos en el router para el 443 del reverse-proxy y del 8096 del jellyfin para poder dar acceso fuera de nuestra red…
Nuestro certificado activo
CGNAT y puertos
Para los que están dentro de un CGNAT, deben hablar con su ISP y comentarles que los saquen de la CGNAT, para poder abrir sus puertos tranquilamente.
Puede pasar que nuestra IP cambie, y todos nuestros puertos se cierren, y nos volvamos locos.
La idea es apagar el router por 15 minutos para que obtenga una IP fuera del CGNAT
.
En un inicio sin dar solución, use ngrok para comprobar que todo andara bien y así fue, con eso me di cuenta de que era mi ISP, además de, abrir el jellyfin con el navegador TOR usando ngrok
en ese momento.
Comentar que el jail del reverse proxy se le establecio ip dinámica con:
-
ifconfig_vnet0="SYNCDHCP"
pero, estaba con amarre ip-mac en el router, debído a que en cierto momento ese jail del reverse-proxy se quedo sin internet, cosas internas ya de mi red.
Y por último se actualizo la dirección ip de duckdns a la nueva, quizás se puedo automatizar mejor con un script o cron. |